diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a79973ee..c6b06079 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,7 @@ repos: hooks: - id: pytest-check name: pytest-check - entry: coverage run -m pytest tests/unit + entry: coverage run --source=. -m pytest tests/unit language: python pass_filenames: false types: [python] diff --git a/aixplain/cli_groups.py b/aixplain/cli_groups.py index ea5e28be..33192175 100644 --- a/aixplain/cli_groups.py +++ b/aixplain/cli_groups.py @@ -21,28 +21,43 @@ CLI Runner """ import click -from aixplain.factories.cli.model_factory_cli import list_host_machines, list_functions, create_asset_repo, asset_repo_login, onboard_model, deploy_huggingface_model, get_huggingface_model_status, list_gpus +from aixplain.factories.cli.model_factory_cli import ( + list_host_machines, + list_functions, + create_asset_repo, + asset_repo_login, + onboard_model, + deploy_huggingface_model, + get_huggingface_model_status, + list_gpus, +) -@click.group('cli') + +@click.group("cli") def cli(): pass -@click.group('list') + +@click.group("list") def list(): pass -@click.group('get') + +@click.group("get") def get(): pass -@click.group('create') + +@click.group("create") def create(): pass -@click.group('onboard') + +@click.group("onboard") def onboard(): pass + cli.add_command(list) cli.add_command(get) cli.add_command(create) diff --git a/aixplain/enums/__init__.py b/aixplain/enums/__init__.py index 4da09643..947d59a9 100644 --- a/aixplain/enums/__init__.py +++ b/aixplain/enums/__init__.py @@ -13,3 +13,4 @@ from .supplier import Supplier from .sort_by import SortBy from .sort_order import SortOrder +from .response_status import ResponseStatus \ No newline at end of file diff --git a/aixplain/enums/asset_status.py b/aixplain/enums/asset_status.py index 134af26e..994212fb 100644 --- a/aixplain/enums/asset_status.py +++ b/aixplain/enums/asset_status.py @@ -24,20 +24,22 @@ from enum import Enum from typing import Text + class AssetStatus(Text, Enum): - HIDDEN = 'hidden' - SCHEDULED = 'scheduled' - ONBOARDING = 'onboarding' - ONBOARDED = 'onboarded' - PENDING = 'pending' - FAILED = 'failed' - TRAINING = 'training' - REJECTED = 'rejected' - ENABLING = 'enabling' - DELETING = 'deleting' - DISABLED = 'disabled' - DELETED = 'deleted' - IN_PROGRESS = 'in_progress' - COMPLETED = 'completed' - CANCELING = 'canceling' - CANCELED = 'canceled' \ No newline at end of file + DRAFT = "draft" + HIDDEN = "hidden" + SCHEDULED = "scheduled" + ONBOARDING = "onboarding" + ONBOARDED = "onboarded" + PENDING = "pending" + FAILED = "failed" + TRAINING = "training" + REJECTED = "rejected" + ENABLING = "enabling" + DELETING = "deleting" + DISABLED = "disabled" + DELETED = "deleted" + IN_PROGRESS = "in_progress" + COMPLETED = "completed" + CANCELING = "canceling" + CANCELED = "canceled" diff --git a/aixplain/enums/data_subtype.py b/aixplain/enums/data_subtype.py index 106b04cb..ef11cab5 100644 --- a/aixplain/enums/data_subtype.py +++ b/aixplain/enums/data_subtype.py @@ -32,6 +32,6 @@ class DataSubtype(Enum): RACE = "race" SPLIT = "split" TOPIC = "topic" - + def __str__(self): return self._value_ diff --git a/aixplain/enums/function.py b/aixplain/enums/function.py index 9a6f47d4..a6d2e40a 100644 --- a/aixplain/enums/function.py +++ b/aixplain/enums/function.py @@ -55,7 +55,7 @@ def load_functions(): if input_data_object["required"] is True }, "output": {output_data_object["dataType"] for output_data_object in function["output"]}, - "spec": function + "spec": function, } for function in resp["items"] } diff --git a/aixplain/enums/response_status.py b/aixplain/enums/response_status.py new file mode 100644 index 00000000..d2810753 --- /dev/null +++ b/aixplain/enums/response_status.py @@ -0,0 +1,31 @@ +__author__ = "thiagocastroferreira" + +""" +Copyright 2024 The aiXplain SDK authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: Duraikrishna Selvaraju, Thiago Castro Ferreira, Shreyas Sharma and Lucas Pavanelli +Date: February 21st 2024 +Description: + Asset Enum +""" + +from enum import Enum +from typing import Text + + +class ResponseStatus(Text, Enum): + IN_PROGRESS = "IN_PROGRESS" + SUCCESS = "SUCCESS" + FAILED = "FAILED" diff --git a/aixplain/enums/storage_type.py b/aixplain/enums/storage_type.py index dca35f29..672d0c67 100644 --- a/aixplain/enums/storage_type.py +++ b/aixplain/enums/storage_type.py @@ -30,4 +30,4 @@ class StorageType(Enum): FILE = "file" def __str__(self): - return self._value_ \ No newline at end of file + return self._value_ diff --git a/aixplain/enums/supplier.py b/aixplain/enums/supplier.py index 5d3e137d..ecc29998 100644 --- a/aixplain/enums/supplier.py +++ b/aixplain/enums/supplier.py @@ -48,6 +48,7 @@ def load_suppliers(): headers = {"x-aixplain-key": aixplain_key, "Content-Type": "application/json"} else: headers = {"x-api-key": api_key, "Content-Type": "application/json"} + logging.debug(f"Start service for GET API Creation - {url} - {headers}") r = _request_with_retry("get", url, headers=headers) if not 200 <= r.status_code < 300: raise Exception( diff --git a/aixplain/factories/agent_factory/__init__.py b/aixplain/factories/agent_factory/__init__.py index 2a16e191..c56d1fd8 100644 --- a/aixplain/factories/agent_factory/__init__.py +++ b/aixplain/factories/agent_factory/__init__.py @@ -34,7 +34,6 @@ from aixplain.utils import config from typing import Dict, List, Optional, Text, Union -from aixplain.factories.agent_factory.utils import build_agent, validate_llm, validate_name from aixplain.utils.file_utils import _request_with_retry from urllib.parse import urljoin @@ -65,74 +64,49 @@ def create( Returns: Agent: created Agent """ - validate_name(name) - # validate LLM ID - validate_llm(llm_id) + from aixplain.factories.agent_factory.utils import build_agent + agent = None + url = urljoin(config.BACKEND_URL, "sdk/agents") + headers = {"x-api-key": api_key} + + if isinstance(supplier, dict): + supplier = supplier["code"] + elif isinstance(supplier, Supplier): + supplier = supplier.value["code"] + + payload = { + "name": name, + "assets": [tool.to_dict() for tool in tools], + "description": description, + "supplier": supplier, + "version": version, + "llmId": llm_id, + "status": "draft", + } + agent = build_agent(payload=payload, api_key=api_key) + agent.validate() + response = "Unspecified error" try: - agent = None - url = urljoin(config.BACKEND_URL, "sdk/agents") - headers = {"x-api-key": api_key} - - if isinstance(supplier, dict): - supplier = supplier["code"] - elif isinstance(supplier, Supplier): - supplier = supplier.value["code"] - - tool_payload = [] - for tool in tools: - if isinstance(tool, ModelTool): - tool.validate() - tool_payload.append( - { - "function": tool.function.value if tool.function is not None else None, - "type": "model", - "description": tool.description, - "supplier": tool.supplier.value["code"] if tool.supplier else None, - "version": tool.version if tool.version else None, - "assetId": tool.model, - } - ) - elif isinstance(tool, PipelineTool): - tool.validate() - tool_payload.append( - { - "assetId": tool.pipeline, - "description": tool.description, - "type": "pipeline", - } - ) - else: - raise Exception("Agent Creation Error: Tool type not supported.") - - payload = { - "name": name, - "assets": tool_payload, - "description": description, - "supplier": supplier, - "version": version, - "llmId": llm_id, - } - - logging.info(f"Start service for POST Create Agent - {url} - {headers} - {json.dumps(payload)}") + logging.debug(f"Start service for POST Create Agent - {url} - {headers} - {json.dumps(payload)}") r = _request_with_retry("post", url, headers=headers, json=payload) - if 200 <= r.status_code < 300: - response = r.json() - agent = build_agent(payload=response, api_key=api_key) - else: - error = r.json() - error_msg = "Agent Onboarding Error: Please contact the administrators." - if "message" in error: - msg = error["message"] - if error["message"] == "err.name_already_exists": - msg = "Agent name already exists." - elif error["message"] == "err.asset_is_not_available": - msg = "Some tools are not available." - error_msg = f"Agent Onboarding Error (HTTP {r.status_code}): {msg}" - logging.exception(error_msg) - raise Exception(error_msg) - except Exception as e: - raise Exception(e) + response = r.json() + except Exception: + raise Exception("Agent Onboarding Error: Please contact the administrators.") + + if 200 <= r.status_code < 300: + agent = build_agent(payload=response, api_key=api_key) + else: + error_msg = f"Agent Onboarding Error: {response}" + if "message" in response: + msg = response["message"] + if response["message"] == "err.name_already_exists": + msg = "Agent name already exists." + elif response["message"] == "err.asset_is_not_available": + msg = "Some tools are not available." + error_msg = f"Agent Onboarding Error (HTTP {r.status_code}): {msg}" + logging.exception(error_msg) + raise Exception(error_msg) return agent @classmethod @@ -141,6 +115,7 @@ def create_model_tool( model: Optional[Union[Model, Text]] = None, function: Optional[Union[Function, Text]] = None, supplier: Optional[Union[Supplier, Text]] = None, + description: Text = "", ) -> ModelTool: """Create a new model tool.""" if function is not None and isinstance(function, str): @@ -154,7 +129,7 @@ def create_model_tool( break if isinstance(supplier, str): supplier = None - return ModelTool(function=function, supplier=supplier, model=model) + return ModelTool(function=function, supplier=supplier, model=model, description=description) @classmethod def create_pipeline_tool(cls, description: Text, pipeline: Union[Pipeline, Text]) -> PipelineTool: @@ -164,37 +139,42 @@ def create_pipeline_tool(cls, description: Text, pipeline: Union[Pipeline, Text] @classmethod def list(cls) -> Dict: """List all agents available in the platform.""" + from aixplain.factories.agent_factory.utils import build_agent + url = urljoin(config.BACKEND_URL, "sdk/agents") headers = {"x-api-key": config.TEAM_API_KEY, "Content-Type": "application/json"} + resp = {} payload = {} logging.info(f"Start service for GET List Agents - {url} - {headers} - {json.dumps(payload)}") try: r = _request_with_retry("get", url, headers=headers) resp = r.json() + except Exception: + raise Exception("Agent Listing Error: Please contact the administrators.") - if 200 <= r.status_code < 300: - agents, page_total, total = [], 0, 0 - results = resp - page_total = len(results) - total = len(results) - logging.info(f"Response for GET List Agents - Page Total: {page_total} / Total: {total}") - for agent in results: - agents.append(build_agent(agent)) - return {"results": agents, "page_total": page_total, "page_number": 0, "total": total} - else: - error_msg = "Agent Listing Error: Please contact the administrators." - if "message" in resp: - msg = resp["message"] - error_msg = f"Agent Listing Error (HTTP {r.status_code}): {msg}" - logging.exception(error_msg) - raise Exception(error_msg) - except Exception as e: - raise Exception(e) + if 200 <= r.status_code < 300: + agents, page_total, total = [], 0, 0 + results = resp + page_total = len(results) + total = len(results) + logging.info(f"Response for GET List Agents - Page Total: {page_total} / Total: {total}") + for agent in results: + agents.append(build_agent(agent)) + return {"results": agents, "page_total": page_total, "page_number": 0, "total": total} + else: + error_msg = "Agent Listing Error: Please contact the administrators." + if isinstance(resp, dict) and "message" in resp: + msg = resp["message"] + error_msg = f"Agent Listing Error (HTTP {r.status_code}): {msg}" + logging.exception(error_msg) + raise Exception(error_msg) @classmethod def get(cls, agent_id: Text, api_key: Optional[Text] = None) -> Agent: """Get agent by id.""" + from aixplain.factories.agent_factory.utils import build_agent + url = urljoin(config.BACKEND_URL, f"sdk/agents/{agent_id}") if config.AIXPLAIN_API_KEY != "": headers = {"x-aixplain-key": f"{config.AIXPLAIN_API_KEY}", "Content-Type": "application/json"} diff --git a/aixplain/factories/agent_factory/utils.py b/aixplain/factories/agent_factory/utils.py index d86982ef..e5e73dc4 100644 --- a/aixplain/factories/agent_factory/utils.py +++ b/aixplain/factories/agent_factory/utils.py @@ -12,58 +12,43 @@ def build_agent(payload: Dict, api_key: Text = config.TEAM_API_KEY) -> Agent: """Instantiate a new agent in the platform.""" - tools = payload["assets"] - for i, tool in enumerate(tools): + tools_dict = payload["assets"] + tools = [] + for tool in tools_dict: if tool["type"] == "model": - for supplier in Supplier: + supplier = "aixplain" + for supplier_ in Supplier: if tool["supplier"] is not None and tool["supplier"].lower() in [ - supplier.value["code"].lower(), - supplier.value["name"].lower(), + supplier_.value["code"].lower(), + supplier_.value["name"].lower(), ]: - tool["supplier"] = supplier + supplier = supplier_ break tool = ModelTool( - function=Function(tool["function"]) if tool["function"] is not None else None, - supplier=tool["supplier"], + function=Function(tool.get("function", None)), + supplier=supplier, version=tool["version"], model=tool["assetId"], + description=tool.get("description", ""), ) elif tool["type"] == "pipeline": tool = PipelineTool(description=tool["description"], pipeline=tool["assetId"]) else: raise Exception("Agent Creation Error: Tool type not supported.") - tools[i] = tool + tools.append(tool) agent = Agent( - id=payload["id"], - name=payload["name"] if "name" in payload else "", + id=payload["id"] if "id" in payload else "", + name=payload.get("name", ""), tools=tools, - description=payload["description"] if "description" in payload else "", - supplier=payload["teamId"] if "teamId" in payload else None, - version=payload["version"] if "version" in payload else None, - cost=payload["cost"] if "cost" in payload else None, - llm_id=payload["llmId"] if "llmId" in payload else GPT_4o_ID, + description=payload.get("description", ""), + supplier=payload.get("teamId", None), + version=payload.get("version", None), + cost=payload.get("cost", None), + llm_id=payload.get("llmId", GPT_4o_ID), api_key=api_key, status=AssetStatus(payload["status"]), ) agent.url = urljoin(config.BACKEND_URL, f"sdk/agents/{agent.id}/run") return agent - - -def validate_llm(model_id: Text) -> None: - from aixplain.factories.model_factory import ModelFactory - - try: - llm = ModelFactory.get(model_id) - assert llm.function == Function.TEXT_GENERATION, "Large Language Model must be a text generation model." - except Exception: - raise Exception(f"Large Language Model with ID '{model_id}' not found.") - - -def validate_name(name: Text) -> None: - import re - - assert ( - re.match("^[a-zA-Z0-9 ]*$", name) is not None - ), "Agent Creation Error: Agent name must not contain special characters." diff --git a/aixplain/factories/cli/model_factory_cli.py b/aixplain/factories/cli/model_factory_cli.py index b83d61cc..9c69ca4f 100644 --- a/aixplain/factories/cli/model_factory_cli.py +++ b/aixplain/factories/cli/model_factory_cli.py @@ -26,11 +26,11 @@ import click import yaml + @click.command("hosts") -@click.option("--api-key", default=None, - help="TEAM_API_KEY if not already set in environment") +@click.option("--api-key", default=None, help="TEAM_API_KEY if not already set in environment") def list_host_machines(api_key: Optional[Text] = None) -> None: - """CLI wrapper function for the LIST_HOST_MACHINES function in + """CLI wrapper function for the LIST_HOST_MACHINES function in ModelFactory. Args: @@ -43,16 +43,15 @@ def list_host_machines(api_key: Optional[Text] = None) -> None: ret_val_yaml = yaml.dump(ret_val) click.echo(ret_val_yaml) + @click.command("functions") -@click.option("--verbose", is_flag=True, - help="List all function details, False by default.") -@click.option("--api-key", default=None, - help="TEAM_API_KEY if not already set in environment.") +@click.option("--verbose", is_flag=True, help="List all function details, False by default.") +@click.option("--api-key", default=None, help="TEAM_API_KEY if not already set in environment.") def list_functions(verbose: bool, api_key: Optional[Text] = None) -> None: """CLI wrapper function for the LIST_FUNCTIONS function in ModelFactory. Args: - verbose (Boolean, optional): Set to True if a detailed response + verbose (Boolean, optional): Set to True if a detailed response is desired; is otherwise False by default. api_key (Text, optional): Team API key. Defaults to None. Returns: @@ -62,9 +61,9 @@ def list_functions(verbose: bool, api_key: Optional[Text] = None) -> None: ret_val_yaml = yaml.dump(ret_val) click.echo(ret_val_yaml) + @click.command("gpus") -@click.option("--api-key", default=None, - help="TEAM_API_KEY if not already set in environment.") +@click.option("--api-key", default=None, help="TEAM_API_KEY if not already set in environment.") def list_gpus(api_key: Optional[Text] = None) -> None: """CLI wrapper function for the LIST_GPUS function in ModelFactory. @@ -77,22 +76,28 @@ def list_gpus(api_key: Optional[Text] = None) -> None: ret_val_yaml = yaml.dump(ret_val) click.echo(ret_val_yaml) + @click.command("image-repo") @click.option("--name", help="Model name.") @click.option("--description", help="Description of model.") @click.option("--function", help="Function name obtained from LIST_FUNCTIONS.") -@click.option("--source-language", default="en", - help="Model source language in 2-character 639-1 code or 3-character 639-3 code.") +@click.option( + "--source-language", default="en", help="Model source language in 2-character 639-1 code or 3-character 639-3 code." +) @click.option("--input-modality", help="Input type (text, video, image, etc.)") @click.option("--output-modality", help="Output type (text, video, image, etc.)") @click.option("--documentation-url", default="", help="Link to model documentation.") -@click.option("--api-key", default=None, - help="TEAM_API_KEY if not already set in environment.") -def create_asset_repo(name: Text, description: Text, function: Text, - source_language: Text, input_modality: Text, - output_modality: Text, - documentation_url: Optional[Text] = "", - api_key: Optional[Text] = None) -> None: +@click.option("--api-key", default=None, help="TEAM_API_KEY if not already set in environment.") +def create_asset_repo( + name: Text, + description: Text, + function: Text, + source_language: Text, + input_modality: Text, + output_modality: Text, + documentation_url: Optional[Text] = "", + api_key: Optional[Text] = None, +) -> None: """CLI wrapper function for the CREATE_ASSET_REPO function in ModelFactory. Args: @@ -109,16 +114,15 @@ def create_asset_repo(name: Text, description: Text, function: Text, Returns: None """ - ret_val = ModelFactory.create_asset_repo(name, description, function, - source_language, input_modality, - output_modality, documentation_url, - api_key) + ret_val = ModelFactory.create_asset_repo( + name, description, function, source_language, input_modality, output_modality, documentation_url, api_key + ) ret_val_yaml = yaml.dump(ret_val) click.echo(ret_val_yaml) + @click.command("image-repo-login") -@click.option("--api-key", default=None, - help="TEAM_API_KEY if not already set in environment.") +@click.option("--api-key", default=None, help="TEAM_API_KEY if not already set in environment.") def asset_repo_login(api_key: Optional[Text] = None) -> None: """CLI wrapper function for the ASSET_REPO_LOGIN function in ModelFactory. @@ -132,15 +136,16 @@ def asset_repo_login(api_key: Optional[Text] = None) -> None: ret_val_yaml = yaml.dump(ret_val) click.echo(ret_val_yaml) + @click.command("model") @click.option("--model-id", help="Model ID from CREATE_IMAGE_REPO.") @click.option("--image-tag", help="The tag of the image that you would like hosted.") @click.option("--image-hash", help="The hash of the image you would like onboarded.") @click.option("--host-machine", default="", help="The machine on which to host the model.") @click.option("--api-key", default=None, help="TEAM_API_KEY if not already set in environment.") -def onboard_model(model_id: Text, image_tag: Text, image_hash: Text, - host_machine: Optional[Text] = "", - api_key: Optional[Text] = None) -> None: +def onboard_model( + model_id: Text, image_tag: Text, image_hash: Text, host_machine: Optional[Text] = "", api_key: Optional[Text] = None +) -> None: """CLI wrapper function for the ONBOARD_MODEL function in ModelFactory. Args: @@ -150,22 +155,25 @@ def onboard_model(model_id: Text, image_tag: Text, image_hash: Text, Returns: None - """ - ret_val = ModelFactory.onboard_model(model_id, image_tag, image_hash, - host_machine, api_key) + """ + ret_val = ModelFactory.onboard_model(model_id, image_tag, image_hash, host_machine, api_key) ret_val_yaml = yaml.dump(ret_val) click.echo(ret_val_yaml) + @click.command("hf-model") @click.option("--name", help="User-defined name for Hugging Face model.") @click.option("--hf-repo-id", help="Repository ID from Hugging Face in {supplier}/{model name} form.") @click.option("--revision", default="", help="Commit hash of repository.") @click.option("--hf-token", default=None, help="Hugging Face token used to authenticate to this model.") @click.option("--api-key", default=None, help="TEAM_API_KEY if not already set in environment.") -def deploy_huggingface_model(name: Text, hf_repo_id: Text, - hf_token: Optional[Text] = None, - revision: Optional[Text] = None, - api_key: Optional[Text] = None) -> None: +def deploy_huggingface_model( + name: Text, + hf_repo_id: Text, + hf_token: Optional[Text] = None, + revision: Optional[Text] = None, + api_key: Optional[Text] = None, +) -> None: """CLI wrapper function for the DEPLOY_HUGGINGFACE_MODEL function in ModelFactory. Args: @@ -179,6 +187,7 @@ def deploy_huggingface_model(name: Text, hf_repo_id: Text, ret_val_yaml = yaml.dump(ret_val) click.echo(ret_val_yaml) + @click.command("hf-model-status") @click.option("--model-id", help="Model ID from DEPLOY_HUGGINGFACE_MODEL.") @click.option("--api-key", default=None, help="TEAM_API_KEY if not already set in environment.") diff --git a/aixplain/factories/file_factory.py b/aixplain/factories/file_factory.py index adbebcd3..1a29ac11 100644 --- a/aixplain/factories/file_factory.py +++ b/aixplain/factories/file_factory.py @@ -91,7 +91,7 @@ def check_storage_type(cls, input_link: Any) -> StorageType: Returns: StorageType: URL, TEXT or FILE """ - if os.path.exists(input_link) is True: + if os.path.exists(input_link) is True and os.path.isfile(input_link) is True: return StorageType.FILE elif ( input_link.startswith("s3://") @@ -145,5 +145,5 @@ def create( """ assert ( license is not None if is_temp is False else True - ), "File Asset Creation Error: To upload a non-temporary file, you need to specify the `license`." + ), "File Asset Creation Error: To upload a non-temporary file, you need to specify the `license`." return cls.upload(local_path=local_path, tags=tags, license=license, is_temp=is_temp) diff --git a/aixplain/factories/finetune_factory/__init__.py b/aixplain/factories/finetune_factory/__init__.py index 7b05b759..7a23c527 100644 --- a/aixplain/factories/finetune_factory/__init__.py +++ b/aixplain/factories/finetune_factory/__init__.py @@ -88,11 +88,11 @@ def create( assert ( train_percentage + dev_percentage <= 100 ), f"Create FineTune: Train percentage + dev percentage ({train_percentage + dev_percentage}) must be less than or equal to one" - + for i, dataset in enumerate(dataset_list): if isinstance(dataset, str) is True: dataset_list[i] = DatasetFactory.get(dataset_id=dataset) - + if isinstance(model, str) is True: model = ModelFactory.get(model_id=model) diff --git a/aixplain/factories/model_factory.py b/aixplain/factories/model_factory.py index 209ff75d..b6588023 100644 --- a/aixplain/factories/model_factory.py +++ b/aixplain/factories/model_factory.py @@ -222,7 +222,7 @@ def _get_assets_from_page( @classmethod def list( cls, - function: Function, + function: Optional[Function] = None, query: Optional[Text] = "", suppliers: Optional[Union[Supplier, List[Supplier]]] = None, source_languages: Optional[Union[Language, List[Language]]] = None, diff --git a/aixplain/factories/pipeline_factory/utils.py b/aixplain/factories/pipeline_factory/utils.py index 7911c370..08954571 100644 --- a/aixplain/factories/pipeline_factory/utils.py +++ b/aixplain/factories/pipeline_factory/utils.py @@ -82,6 +82,14 @@ def build_from_response(response: Dict, load_architecture: bool = False) -> Pipe for output_param in node_json["outputValues"] if output_param["code"] not in node.outputs ] + if "customInputs" in node_json: + for custom_input in node_json["customInputs"]: + node.inputs.create_param( + data_type=custom_input.get("dataType"), + code=custom_input["code"], + value=custom_input.get("value"), + is_required=custom_input.get("isRequired", True), + ) node.number = node_json["number"] node.label = node_json["label"] pipeline.add_node(node) diff --git a/aixplain/factories/team_agent_factory/__init__.py b/aixplain/factories/team_agent_factory/__init__.py index 72d47c03..3f65b4b0 100644 --- a/aixplain/factories/team_agent_factory/__init__.py +++ b/aixplain/factories/team_agent_factory/__init__.py @@ -25,8 +25,6 @@ import logging from aixplain.enums.supplier import Supplier -from aixplain.factories.agent_factory import AgentFactory -from aixplain.factories.agent_factory.utils import validate_llm, validate_name from aixplain.modules.agent import Agent from aixplain.modules.team_agent import TeamAgent from aixplain.utils import config @@ -50,67 +48,73 @@ def create( use_mentalist_and_inspector: bool = True, ) -> TeamAgent: """Create a new team agent in the platform.""" - validate_name(name) - # validate LLM ID - validate_llm(llm_id) assert len(agents) > 0, "TeamAgent Onboarding Error: At least one agent must be provided." for agent in agents: if isinstance(agent, Text) is True: try: + from aixplain.factories.agent_factory import AgentFactory + agent = AgentFactory.get(agent) except Exception: raise Exception(f"TeamAgent Onboarding Error: Agent {agent} does not exist.") else: + from aixplain.modules.agent import Agent + assert isinstance(agent, Agent), "TeamAgent Onboarding Error: Agents must be instances of Agent class" mentalist_and_inspector_llm_id = None if use_mentalist_and_inspector is True: mentalist_and_inspector_llm_id = llm_id + + team_agent = None + url = urljoin(config.BACKEND_URL, "sdk/agent-communities") + headers = {"x-api-key": api_key} + + if isinstance(supplier, dict): + supplier = supplier["code"] + elif isinstance(supplier, Supplier): + supplier = supplier.value["code"] + + agent_list = [] + for idx, agent in enumerate(agents): + agent_list.append({"assetId": agent.id, "number": idx, "type": "AGENT", "label": "AGENT"}) + + payload = { + "name": name, + "agents": agent_list, + "links": [], + "description": description, + "llmId": llm_id, + "supervisorId": llm_id, + "plannerId": mentalist_and_inspector_llm_id, + "supplier": supplier, + "version": version, + "status": "draft", + } + + team_agent = build_team_agent(payload=payload, api_key=api_key) + team_agent.validate() + response = "Unspecified error" try: - team_agent = None - url = urljoin(config.BACKEND_URL, "sdk/agent-communities") - headers = {"x-api-key": api_key} - - if isinstance(supplier, dict): - supplier = supplier["code"] - elif isinstance(supplier, Supplier): - supplier = supplier.value["code"] - - agent_list = [] - for idx, agent in enumerate(agents): - agent_list.append({"assetId": agent.id, "number": idx, "type": "AGENT", "label": "AGENT"}) - - payload = { - "name": name, - "agents": agent_list, - "links": [], - "description": description, - "llmId": llm_id, - "supervisorId": llm_id, - "plannerId": mentalist_and_inspector_llm_id, - "supplier": supplier, - "version": version, - } - - logging.info(f"Start service for POST Create TeamAgent - {url} - {headers} - {json.dumps(payload)}") + logging.debug(f"Start service for POST Create TeamAgent - {url} - {headers} - {json.dumps(payload)}") r = _request_with_retry("post", url, headers=headers, json=payload) - if 200 <= r.status_code < 300: - response = r.json() - team_agent = build_team_agent(payload=response, api_key=api_key) - else: - error = r.json() - error_msg = "TeamAgent Onboarding Error: Please contact the administrators." - if "message" in error: - msg = error["message"] - if error["message"] == "err.name_already_exists": - msg = "TeamAgent name already exists." - elif error["message"] == "err.asset_is_not_available": - msg = "Some tools are not available." - error_msg = f"TeamAgent Onboarding Error (HTTP {r.status_code}): {msg}" - logging.exception(error_msg) - raise Exception(error_msg) + response = r.json() except Exception as e: raise Exception(e) + + if 200 <= r.status_code < 300: + team_agent = build_team_agent(payload=response, api_key=api_key) + else: + error_msg = f"{response}" + if "message" in response: + msg = response["message"] + if response["message"] == "err.name_already_exists": + msg = "TeamAgent name already exists." + elif response["message"] == "err.asset_is_not_available": + msg = "Some tools are not available." + error_msg = f"TeamAgent Onboarding Error (HTTP {r.status_code}): {msg}" + logging.exception(error_msg) + raise Exception(error_msg) return team_agent @classmethod @@ -119,33 +123,34 @@ def list(cls) -> Dict: url = urljoin(config.BACKEND_URL, "sdk/agent-communities") headers = {"x-api-key": config.TEAM_API_KEY, "Content-Type": "application/json"} + resp = {} payload = {} logging.info(f"Start service for GET List Agents - {url} - {headers} - {json.dumps(payload)}") try: r = _request_with_retry("get", url, headers=headers) resp = r.json() + except Exception: + raise Exception("Team Agent Listing Error: Please contact the administrators.") - if 200 <= r.status_code < 300: - agents, page_total, total = [], 0, 0 - results = resp - page_total = len(results) - total = len(results) - logging.info(f"Response for GET List Agents - Page Total: {page_total} / Total: {total}") - for agent in results: - agents.append(build_team_agent(agent)) - return {"results": agents, "page_total": page_total, "page_number": 0, "total": total} - else: - error_msg = "Agent Listing Error: Please contact the administrators." - if "message" in resp: - msg = resp["message"] - error_msg = f"Agent Listing Error (HTTP {r.status_code}): {msg}" - logging.exception(error_msg) - raise Exception(error_msg) - except Exception as e: - raise Exception(e) + if 200 <= r.status_code < 300: + agents, page_total, total = [], 0, 0 + results = resp + page_total = len(results) + total = len(results) + logging.info(f"Response for GET List Agents - Page Total: {page_total} / Total: {total}") + for agent in results: + agents.append(build_team_agent(agent)) + return {"results": agents, "page_total": page_total, "page_number": 0, "total": total} + else: + error_msg = "Agent Listing Error: Please contact the administrators." + if isinstance(resp, dict) and "message" in resp: + msg = resp["message"] + error_msg = f"Agent Listing Error (HTTP {r.status_code}): {msg}" + logging.exception(error_msg) + raise Exception(error_msg) @classmethod - def get(cls, agent_id: Text, api_key: Optional[Text] = None) -> Agent: + def get(cls, agent_id: Text, api_key: Optional[Text] = None) -> TeamAgent: """Get agent by id.""" url = urljoin(config.BACKEND_URL, f"sdk/agent-communities/{agent_id}") if config.AIXPLAIN_API_KEY != "": @@ -153,14 +158,18 @@ def get(cls, agent_id: Text, api_key: Optional[Text] = None) -> Agent: else: api_key = api_key if api_key is not None else config.TEAM_API_KEY headers = {"x-api-key": api_key, "Content-Type": "application/json"} - logging.info(f"Start service for GET Agent - {url} - {headers}") - r = _request_with_retry("get", url, headers=headers) - resp = r.json() + logging.info(f"Start service for GET Team Agent - {url} - {headers}") + try: + r = _request_with_retry("get", url, headers=headers) + resp = r.json() + except Exception: + raise Exception("Team Agent Get Error: Please contact the administrators.") + if 200 <= r.status_code < 300: return build_team_agent(resp) else: msg = "Please contact the administrators." if "message" in resp: msg = resp["message"] - error_msg = f"Agent Get Error (HTTP {r.status_code}): {msg}" + error_msg = f"Team Agent Get Error (HTTP {r.status_code}): {msg}" raise Exception(error_msg) diff --git a/aixplain/factories/team_agent_factory/utils.py b/aixplain/factories/team_agent_factory/utils.py index 42fa5f6c..da859a43 100644 --- a/aixplain/factories/team_agent_factory/utils.py +++ b/aixplain/factories/team_agent_factory/utils.py @@ -3,7 +3,6 @@ import aixplain.utils.config as config from aixplain.enums.asset_status import AssetStatus from aixplain.modules.team_agent import TeamAgent -from aixplain.factories.agent_factory import AgentFactory from typing import Dict, Text from urllib.parse import urljoin @@ -12,21 +11,24 @@ def build_team_agent(payload: Dict, api_key: Text = config.TEAM_API_KEY) -> TeamAgent: """Instantiate a new team agent in the platform.""" - agents = payload["agents"] - for i, agent in enumerate(agents): + from aixplain.factories.agent_factory import AgentFactory + + agents_dict = payload["agents"] + agents = [] + for i, agent in enumerate(agents_dict): agent = AgentFactory.get(agent["assetId"]) - agents[i] = agent + agents.append(agent) team_agent = TeamAgent( - id=payload["id"], - name=payload["name"] if "name" in payload else "", + id=payload.get("id", ""), + name=payload.get("name", ""), agents=agents, - description=payload["description"] if "description" in payload else "", - supplier=payload["teamId"] if "teamId" in payload else None, - version=payload["version"] if "version" in payload else None, - cost=payload["cost"] if "cost" in payload else None, - llm_id=payload["llmId"] if "llmId" in payload else GPT_4o_ID, - use_mentalist_and_inspector=True if "plannerId" in payload and payload["plannerId"] is not None else False, + description=payload.get("description", ""), + supplier=payload.get("teamId", None), + version=payload.get("version", None), + cost=payload.get("cost", None), + llm_id=payload.get("llmId", GPT_4o_ID), + use_mentalist_and_inspector=True if payload["plannerId"] is not None else False, api_key=api_key, status=AssetStatus(payload["status"]), ) diff --git a/aixplain/factories/wallet_factory.py b/aixplain/factories/wallet_factory.py index b36000f1..01c0ac2e 100644 --- a/aixplain/factories/wallet_factory.py +++ b/aixplain/factories/wallet_factory.py @@ -10,7 +10,7 @@ class WalletFactory: backend_url = config.BACKEND_URL @classmethod - def get(cls, api_key: Text = config.TEAM_API_KEY) -> Wallet: + def get(cls, api_key: Text = config.TEAM_API_KEY) -> Wallet: """Get wallet information""" try: resp = None @@ -22,7 +22,7 @@ def get(cls, api_key: Text = config.TEAM_API_KEY) -> Wallet: resp = r.json() total_balance = float(resp.get("totalBalance", 0.0)) reserved_balance = float(resp.get("reservedBalance", 0.0)) - + return Wallet(total_balance=total_balance, reserved_balance=reserved_balance) except Exception as e: raise Exception(f"Failed to get the wallet credit information. Error: {str(e)}") diff --git a/aixplain/modules/agent/__init__.py b/aixplain/modules/agent/__init__.py index 41bb0a2e..8d7391af 100644 --- a/aixplain/modules/agent/__init__.py +++ b/aixplain/modules/agent/__init__.py @@ -22,10 +22,12 @@ """ import json import logging +import re import time import traceback from aixplain.utils.file_utils import _request_with_retry +from aixplain.enums.function import Function from aixplain.enums.supplier import Supplier from aixplain.enums.asset_status import AssetStatus from aixplain.enums.storage_type import StorageType @@ -66,7 +68,7 @@ def __init__( supplier: Union[Dict, Text, Supplier, int] = "aiXplain", version: Optional[Text] = None, cost: Optional[Dict] = None, - status: AssetStatus = AssetStatus.ONBOARDING, + status: AssetStatus = AssetStatus.DRAFT, **additional_info, ) -> None: """Create an Agent with the necessary information. @@ -91,9 +93,27 @@ def __init__( try: status = AssetStatus(status) except Exception: - status = AssetStatus.ONBOARDING + status = AssetStatus.DRAFT self.status = status + def validate(self) -> None: + """Validate the Agent.""" + from aixplain.factories.model_factory import ModelFactory + + # validate name + assert ( + re.match("^[a-zA-Z0-9 ]*$", self.name) is not None + ), "Agent Creation Error: Agent name must not contain special characters." + + try: + llm = ModelFactory.get(self.llm_id) + assert llm.function == Function.TEXT_GENERATION, "Large Language Model must be a text generation model." + except Exception: + raise Exception(f"Large Language Model with ID '{self.llm_id}' not found.") + + for tool in self.tools: + tool.validate() + def run( self, data: Optional[Union[Dict, Text]] = None, @@ -242,6 +262,18 @@ def run_async( response["error"] = msg return response + def to_dict(self) -> Dict: + return { + "id": self.id, + "name": self.name, + "assets": [tool.to_dict() for tool in self.tools], + "description": self.description, + "supplier": self.supplier.value["code"] if isinstance(self.supplier, Supplier) else self.supplier, + "version": self.version, + "llmId": self.llm_id, + "status": self.status.value, + } + def delete(self) -> None: """Delete Agent service""" try: @@ -249,13 +281,44 @@ def delete(self) -> None: headers = {"x-api-key": config.TEAM_API_KEY, "Content-Type": "application/json"} logging.debug(f"Start service for DELETE Agent - {url} - {headers}") r = _request_with_retry("delete", url, headers=headers) + logging.debug(f"Result of request for DELETE Agent - {r.status_code}") if r.status_code != 200: raise Exception() except Exception: try: response_json = r.json() - message = f"Agent Deletion Error (HTTP {r.status_code}): {response_json.get('message')}." + message = f"Agent Deletion Error (HTTP {r.status_code}): {response_json.get('message', '').strip('{{}}')}." except ValueError: message = f"Agent Deletion Error (HTTP {r.status_code}): There was an error in deleting the agent." logging.error(message) - raise Exception(message) \ No newline at end of file + raise Exception(f"{message}") + + def update(self) -> None: + """Update agent.""" + from aixplain.factories.agent_factory.utils import build_agent + + self.validate() + url = urljoin(config.BACKEND_URL, f"sdk/agents/{self.id}") + headers = {"x-api-key": config.TEAM_API_KEY, "Content-Type": "application/json"} + + payload = self.to_dict() + + logging.debug(f"Start service for PUT Update Agent - {url} - {headers} - {json.dumps(payload)}") + resp = "No specified error." + try: + r = _request_with_retry("put", url, headers=headers, json=payload) + resp = r.json() + except Exception: + raise Exception("Agent Update Error: Please contact the administrators.") + + if 200 <= r.status_code < 300: + return build_agent(resp) + else: + error_msg = f"Agent Update Error (HTTP {r.status_code}): {resp}" + raise Exception(error_msg) + + def deploy(self) -> None: + assert self.status == AssetStatus.DRAFT, "Agent must be in draft status to be deployed." + assert self.status != AssetStatus.ONBOARDED, "Agent is already deployed." + self.status = AssetStatus.ONBOARDED + self.update() diff --git a/aixplain/modules/agent/tool.py b/aixplain/modules/agent/tool.py deleted file mode 100644 index 6651afe7..00000000 --- a/aixplain/modules/agent/tool.py +++ /dev/null @@ -1,59 +0,0 @@ -__author__ = "aiXplain" - -""" -Copyright 2024 The aiXplain SDK authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -Author: Lucas Pavanelli and Thiago Castro Ferreira -Date: May 16th 2024 -Description: - Agentification Class -""" -from typing import Text, Optional - -from aixplain.enums.function import Function -from aixplain.enums.supplier import Supplier - - -class Tool: - """Specialized software or resource designed to assist the AI in executing specific tasks or functions based on user commands. - - Attributes: - name (Text): name of the tool - description (Text): descriptiion of the tool - function (Function): task that the tool performs - supplier (Optional[Union[Dict, Text, Supplier, int]], optional): Preferred supplier to perform the task. Defaults to None. - """ - - def __init__( - self, - name: Text, - description: Text, - function: Function, - supplier: Optional[Supplier] = None, - **additional_info, - ) -> None: - """Specialized software or resource designed to assist the AI in executing specific tasks or functions based on user commands. - - Args: - name (Text): name of the tool - description (Text): descriptiion of the tool - function (Function): task that the tool performs - supplier (Optional[Union[Dict, Text, Supplier, int]], optional): Preferred supplier to perform the task. Defaults to None. - """ - self.name = name - self.description = description - self.function = function - self.supplier = supplier - self.additional_info = additional_info diff --git a/aixplain/modules/agent/tool/__init__.py b/aixplain/modules/agent/tool/__init__.py index 9c7a7a09..01b44dfa 100644 --- a/aixplain/modules/agent/tool/__init__.py +++ b/aixplain/modules/agent/tool/__init__.py @@ -51,3 +51,10 @@ def __init__( self.description = description self.version = version self.additional_info = additional_info + + def to_dict(self): + """Converts the tool to a dictionary.""" + raise NotImplementedError + + def validate(self): + raise NotImplementedError diff --git a/aixplain/modules/agent/tool/model_tool.py b/aixplain/modules/agent/tool/model_tool.py index 3a84c45b..628377a3 100644 --- a/aixplain/modules/agent/tool/model_tool.py +++ b/aixplain/modules/agent/tool/model_tool.py @@ -42,6 +42,7 @@ def __init__( function: Optional[Union[Function, Text]] = None, supplier: Optional[Union[Dict, Supplier]] = None, model: Optional[Union[Text, Model]] = None, + description: Text = "", **additional_info, ) -> None: """Specialized software or resource designed to assist the AI in executing specific tasks or functions based on user commands. @@ -50,11 +51,12 @@ def __init__( function (Optional[Union[Function, Text]]): task that the tool performs. Defaults to None. supplier (Optional[Union[Dict, Supplier]]): Preferred supplier to perform the task. Defaults to None. Defaults to None. model (Optional[Union[Text, Model]]): Model function. Defaults to None. + description (Text): Description of the tool. Defaults to "". """ assert ( function is not None or model is not None ), "Agent Creation Error: Either function or model must be provided when instantiating a tool." - super().__init__("", "", **additional_info) + super().__init__(name="", description=description, **additional_info) if function is not None: if isinstance(function, str): function = Function(function) @@ -77,6 +79,26 @@ def __init__( self.model = model self.function = function + def to_dict(self) -> Dict: + """Converts the tool to a dictionary.""" + supplier = self.supplier + if supplier is not None: + if isinstance(supplier, dict): + supplier = supplier["code"] + elif isinstance(supplier, Supplier): + supplier = supplier.value["code"] + else: + supplier = str(supplier) + + return { + "function": self.function.value if self.function is not None else None, + "type": "model", + "description": self.description, + "supplier": supplier, + "version": self.version if self.version else None, + "assetId": self.model, + } + def validate(self) -> Model: from aixplain.factories.model_factory import ModelFactory diff --git a/aixplain/modules/agent/tool/pipeline_tool.py b/aixplain/modules/agent/tool/pipeline_tool.py index fa8394ea..9ea7a5fb 100644 --- a/aixplain/modules/agent/tool/pipeline_tool.py +++ b/aixplain/modules/agent/tool/pipeline_tool.py @@ -51,6 +51,13 @@ def __init__( pipeline = pipeline.id self.pipeline = pipeline + def to_dict(self): + return { + "assetId": self.pipeline, + "description": self.description, + "type": "pipeline", + } + def validate(self): from aixplain.factories.pipeline_factory import PipelineFactory diff --git a/aixplain/modules/benchmark.py b/aixplain/modules/benchmark.py index 7a674e05..3f77cb07 100644 --- a/aixplain/modules/benchmark.py +++ b/aixplain/modules/benchmark.py @@ -56,7 +56,7 @@ def __init__( description: Text = "", supplier: Text = "aiXplain", version: Text = "1.0", - **additional_info + **additional_info, ) -> None: """Create a Benchmark with the necessary information. @@ -84,7 +84,6 @@ def __init__( def __repr__(self) -> str: return f"" - def start(self) -> BenchmarkJob: """Starts a new benchmark job(run) for the current benchmark @@ -104,4 +103,4 @@ def start(self) -> BenchmarkJob: except Exception as e: error_message = f"Starting Benchmark Job: Error in Creating Benchmark {benhchmark_id} : {e}" logging.error(error_message, exc_info=True) - return None \ No newline at end of file + return None diff --git a/aixplain/modules/benchmark_job.py b/aixplain/modules/benchmark_job.py index 7dae2d96..c06063fc 100644 --- a/aixplain/modules/benchmark_job.py +++ b/aixplain/modules/benchmark_job.py @@ -7,6 +7,7 @@ from pathlib import Path from aixplain.utils.file_utils import _request_with_retry, save_file + class BenchmarkJob: """Benchmark Job Represents a single run of an already created Benchmark. @@ -35,29 +36,29 @@ def __init__(self, id: Text, status: Text, benchmark_id: Text, **additional_info @classmethod def _create_benchmark_job_from_response(cls, response: Dict): return BenchmarkJob(response["jobId"], response["status"], response["benchmark"]["id"]) - + @classmethod def _fetch_current_response(cls, job_id: Text) -> dict: url = urljoin(config.BACKEND_URL, f"sdk/benchmarks/jobs/{job_id}") - if config.AIXPLAIN_API_KEY != "": + if config.AIXPLAIN_API_KEY != "": headers = {"x-aixplain-key": f"{config.AIXPLAIN_API_KEY}", "Content-Type": "application/json"} else: headers = {"Authorization": f"Token {config.TEAM_API_KEY}", "Content-Type": "application/json"} r = _request_with_retry("get", url, headers=headers) resp = r.json() return resp - + def _update_from_response(self, response: dict): - self.status = response['status'] + self.status = response["status"] def __repr__(self) -> str: return f"" - + def check_status(self): response = self._fetch_current_response(self.id) self._update_from_response(response) return self.status - + def download_results_as_csv(self, save_path: Optional[Text] = None, return_dataframe: bool = False): """Get the results of the benchmark job in a CSV format. The results can either be downloaded locally or returned in the form of pandas.DataFrame. @@ -73,7 +74,7 @@ def download_results_as_csv(self, save_path: Optional[Text] = None, return_dataf try: resp = self._fetch_current_response(self.id) logging.info(f"Downloading Benchmark Results: Status of downloading results for {self.id}: {resp}") - if "reportUrl" not in resp or resp['reportUrl'] == "": + if "reportUrl" not in resp or resp["reportUrl"] == "": logging.error( f"Downloading Benchmark Results: Can't get download results as they aren't generated yet. Please wait for a while." ) @@ -92,9 +93,9 @@ def download_results_as_csv(self, save_path: Optional[Text] = None, return_dataf error_message = f"Downloading Benchmark Results: Error in Downloading Benchmark Results : {e}" logging.error(error_message, exc_info=True) raise Exception(error_message) - + def __simplify_scores(self, scores): - simplified_score_list = [] + simplified_score_list = [] for model_id, model_info in scores.items(): model_scores = model_info["rawScores"] # model = Mode @@ -104,9 +105,6 @@ def __simplify_scores(self, scores): simplified_score_list.append(row) return simplified_score_list - - - def get_scores(self, return_simplified=True, return_as_dataframe=True): try: resp = self._fetch_current_response(self.id) @@ -115,13 +113,13 @@ def get_scores(self, return_simplified=True, return_as_dataframe=True): for iteration_info in iterations: model_id = iteration_info["pipeline"] model_info = { - "creditsUsed" : round(iteration_info.get("credits", 0),5), - "timeSpent" : round(iteration_info.get("runtime", 0),2), - "status" : iteration_info["status"], - "rawScores" : iteration_info["scores"], + "creditsUsed": round(iteration_info.get("credits", 0), 5), + "timeSpent": round(iteration_info.get("runtime", 0), 2), + "status": iteration_info["status"], + "rawScores": iteration_info["scores"], } scores[model_id] = model_info - + if return_simplified: simplified_scores = self.__simplify_scores(scores) if return_as_dataframe: @@ -133,8 +131,7 @@ def get_scores(self, return_simplified=True, return_as_dataframe=True): error_message = f"Benchmark scores: Error in Getting benchmark scores: {e}" logging.error(error_message, exc_info=True) raise Exception(error_message) - - + def get_failuire_rate(self, return_as_dataframe=True): try: scores = self.get_scores(return_simplified=False) @@ -143,10 +140,10 @@ def get_failuire_rate(self, return_as_dataframe=True): if len(model_info["rawScores"]) == 0: failure_rates[model_id] = 0 continue - score_info = model_info["rawScores"][0] + score_info = model_info["rawScores"][0] num_succesful = score_info["count"] num_failed = score_info["failedSegmentsCount"] - failuire_rate = (num_failed * 100) / (num_succesful+num_failed) + failuire_rate = (num_failed * 100) / (num_succesful + num_failed) failure_rates[model_id] = failuire_rate if return_as_dataframe: df = pd.DataFrame() @@ -159,7 +156,7 @@ def get_failuire_rate(self, return_as_dataframe=True): error_message = f"Benchmark scores: Error in Getting benchmark failuire rate: {e}" logging.error(error_message, exc_info=True) raise Exception(error_message) - + def get_all_explanations(self): try: resp = self._fetch_current_response(self) @@ -173,7 +170,7 @@ def get_all_explanations(self): error_message = f"Benchmark scores: Error in Getting benchmark explanations: {e}" logging.error(error_message, exc_info=True) raise Exception(error_message) - + def get_localized_explanations(self, metric_dependant: bool, group_by_task: bool = False): try: raw_explanations = self.get_all_explanations() @@ -205,7 +202,7 @@ def get_localized_explanations(self, metric_dependant: bool, group_by_task: bool else: localized_explanations = raw_explanations["metricInDependent"] if len(localized_explanations) == 0: - localized_explanations = {} + localized_explanations = {} else: localized_explanations = localized_explanations[0] return localized_explanations @@ -213,4 +210,4 @@ def get_localized_explanations(self, metric_dependant: bool, group_by_task: bool except Exception as e: error_message = f"Benchmark scores: Error in Getting benchmark explanations: {e}" logging.error(error_message, exc_info=True) - raise Exception(error_message) \ No newline at end of file + raise Exception(error_message) diff --git a/aixplain/modules/finetune/status.py b/aixplain/modules/finetune/status.py index 4994ce55..5f27aa72 100644 --- a/aixplain/modules/finetune/status.py +++ b/aixplain/modules/finetune/status.py @@ -26,6 +26,7 @@ from dataclasses_json import dataclass_json from typing import Optional, Text + @dataclass_json @dataclass class FinetuneStatus(object): diff --git a/aixplain/modules/model/__init__.py b/aixplain/modules/model/__init__.py index 126df503..104bcb62 100644 --- a/aixplain/modules/model/__init__.py +++ b/aixplain/modules/model/__init__.py @@ -31,6 +31,8 @@ from aixplain.utils.file_utils import _request_with_retry from typing import Union, Optional, Text, Dict from datetime import datetime +from aixplain.modules.model.response import ModelResponse +from aixplain.enums.response_status import ResponseStatus class Model(Asset): @@ -104,6 +106,7 @@ def to_dict(self) -> Dict: return { "id": self.id, "name": self.name, + "description": self.description, "supplier": self.supplier, "additional_info": clean_additional_info, "input_params": self.input_params, @@ -116,7 +119,9 @@ def __repr__(self): except Exception: return f"" - def sync_poll(self, poll_url: Text, name: Text = "model_process", wait_time: float = 0.5, timeout: float = 300) -> Dict: + def sync_poll( + self, poll_url: Text, name: Text = "model_process", wait_time: float = 0.5, timeout: float = 300 + ) -> ModelResponse: """Keeps polling the platform to check whether an asynchronous call is done. Args: @@ -133,7 +138,7 @@ def sync_poll(self, poll_url: Text, name: Text = "model_process", wait_time: flo # keep wait time as 0.2 seconds the minimum wait_time = max(wait_time, 0.2) completed = False - response_body = {"status": "FAILED", "completed": False} + response_body = ModelResponse(status=ResponseStatus.FAILED, completed=False) while not completed and (end - start) < timeout: try: response_body = self.poll(poll_url, name=name) @@ -145,19 +150,23 @@ def sync_poll(self, poll_url: Text, name: Text = "model_process", wait_time: flo if wait_time < 60: wait_time *= 1.1 except Exception as e: - response_body = {"status": "FAILED", "completed": False, "error": "No response from the service."} + response_body = ModelResponse( + status=ResponseStatus.FAILED, completed=False, error_message="No response from the service." + ) logging.error(f"Polling for Model: polling for {name}: {e}") break if response_body["completed"] is True: logging.debug(f"Polling for Model: Final status of polling for {name}: {response_body}") else: - response_body["status"] = "FAILED" + response_body = ModelResponse( + status=ResponseStatus.FAILED, completed=False, error_message="No response from the service." + ) logging.error( f"Polling for Model: Final status of polling for {name}: No response in {timeout} seconds - {response_body}" ) return response_body - def poll(self, poll_url: Text, name: Text = "model_process") -> Dict: + def poll(self, poll_url: Text, name: Text = "model_process") -> ModelResponse: """Poll the platform to check whether an asynchronous call is done. Args: @@ -172,16 +181,31 @@ def poll(self, poll_url: Text, name: Text = "model_process") -> Dict: try: resp = r.json() if resp["completed"] is True: - resp["status"] = "SUCCESS" - if "error" in resp or "supplierError" in resp: - resp["status"] = "FAILED" + status = ResponseStatus.SUCCESS + if "error_message" in resp or "supplierError" in resp: + status = ResponseStatus.FAILED else: - resp["status"] = "IN_PROGRESS" + status = ResponseStatus.IN_PROGRESS logging.debug(f"Single Poll for Model: Status of polling for {name}: {resp}") + return ModelResponse( + status=resp.pop("status", status), + data=resp.pop("data", ""), + details=resp.pop("details", {}), + completed=resp.pop("completed", False), + error_message=resp.pop("error_message", ""), + used_credits=resp.pop("usedCredits", 0), + run_time=resp.pop("runTime", 0), + usage=resp.pop("usage", None), + **resp, + ) except Exception as e: resp = {"status": "FAILED"} logging.error(f"Single Poll for Model: Error of polling for {name}: {e}") - return resp + return ModelResponse( + status=ResponseStatus.FAILED, + error_message=str(e), + completed=False, + ) def run( self, @@ -190,7 +214,7 @@ def run( timeout: float = 300, parameters: Optional[Dict] = None, wait_time: float = 0.5, - ) -> Dict: + ) -> ModelResponse: """Runs a model call. Args: @@ -212,15 +236,27 @@ def run( try: poll_url = response["url"] end = time.time() - response = self.sync_poll(poll_url, name=name, timeout=timeout, wait_time=wait_time) + return self.sync_poll(poll_url, name=name, timeout=timeout, wait_time=wait_time) except Exception as e: msg = f"Error in request for {name} - {traceback.format_exc()}" logging.error(f"Model Run: Error in running for {name}: {e}") end = time.time() - response = {"status": "FAILED", "error": msg, "elapsed_time": end - start} - return response - - def run_async(self, data: Union[Text, Dict], name: Text = "model_process", parameters: Optional[Dict] = None) -> Dict: + response = {"status": "FAILED", "error_message": msg, "runTime": end - start} + return ModelResponse( + status=response.pop("status", ResponseStatus.FAILED), + data=response.pop("data", ""), + details=response.pop("details", {}), + completed=response.pop("completed", False), + error_message=response.pop("error_message", ""), + used_credits=response.pop("usedCredits", 0), + run_time=response.pop("runTime", 0), + usage=response.pop("usage", None), + **response, + ) + + def run_async( + self, data: Union[Text, Dict], name: Text = "model_process", parameters: Optional[Dict] = None + ) -> ModelResponse: """Runs asynchronously a model call. Args: @@ -235,7 +271,15 @@ def run_async(self, data: Union[Text, Dict], name: Text = "model_process", param logging.debug(f"Model Run Async: Start service for {name} - {url}") payload = build_payload(data=data, parameters=parameters) response = call_run_endpoint(payload=payload, url=url, api_key=self.api_key) - return response + return ModelResponse( + status=response.pop("status", ResponseStatus.FAILED), + data=response.pop("data", ""), + details=response.pop("details", {}), + completed=response.pop("completed", False), + error_message=response.pop("error_message", ""), + url=response.pop("url", None), + **response, + ) def check_finetune_status(self, after_epoch: Optional[int] = None): """Check the status of the FineTune model. diff --git a/aixplain/modules/model/llm_model.py b/aixplain/modules/model/llm_model.py index f0b4cef6..600fd32e 100644 --- a/aixplain/modules/model/llm_model.py +++ b/aixplain/modules/model/llm_model.py @@ -28,6 +28,8 @@ from aixplain.modules.model.utils import build_payload, call_run_endpoint from aixplain.utils import config from typing import Union, Optional, List, Text, Dict +from aixplain.modules.model.response import ModelResponse +from aixplain.enums.response_status import ResponseStatus class LLM(Model): @@ -104,7 +106,7 @@ def run( timeout: float = 300, parameters: Optional[Dict] = None, wait_time: float = 0.5, - ) -> Dict: + ) -> ModelResponse: """Synchronously running a Large Language Model (LLM) model. Args: @@ -144,13 +146,24 @@ def run( try: poll_url = response["url"] end = time.time() - response = self.sync_poll(poll_url, name=name, timeout=timeout, wait_time=wait_time) + return self.sync_poll(poll_url, name=name, timeout=timeout, wait_time=wait_time) + except Exception as e: msg = f"Error in request for {name} - {traceback.format_exc()}" logging.error(f"Model Run: Error in running for {name}: {e}") end = time.time() response = {"status": "FAILED", "error": msg, "elapsed_time": end - start} - return response + return ModelResponse( + status=response.pop("status", ResponseStatus.FAILED), + data=response.pop("data", ""), + details=response.pop("details", {}), + completed=response.pop("completed", False), + error_message=response.pop("error_message", ""), + used_credits=response.pop("usedCredits", 0), + run_time=response.pop("runTime", 0), + usage=response.pop("usage", None), + **response, + ) def run_async( self, @@ -163,7 +176,7 @@ def run_async( top_p: float = 1.0, name: Text = "model_process", parameters: Optional[Dict] = None, - ) -> Dict: + ) -> ModelResponse: """Runs asynchronously a model call. Args: @@ -196,4 +209,12 @@ def run_async( ) payload = build_payload(data=data, parameters=parameters) response = call_run_endpoint(payload=payload, url=url, api_key=self.api_key) - return response + return ModelResponse( + status=response.pop("status", ResponseStatus.FAILED), + data=response.pop("data", ""), + details=response.pop("details", {}), + completed=response.pop("completed", False), + error_message=response.pop("error_message", ""), + url=response.pop("url", None), + **response, + ) diff --git a/aixplain/modules/model/response.py b/aixplain/modules/model/response.py new file mode 100644 index 00000000..94ddcb9d --- /dev/null +++ b/aixplain/modules/model/response.py @@ -0,0 +1,75 @@ +from typing import Text, Any, Optional, Dict, List, Union +from aixplain.enums import ResponseStatus + + +class ModelResponse: + """ModelResponse class to store the response of the model run.""" + + def __init__( + self, + status: ResponseStatus, + data: Text = "", + details: Optional[Union[Dict, List]] = {}, + completed: bool = False, + error_message: Text = "", + used_credits: float = 0.0, + run_time: float = 0.0, + usage: Optional[Dict] = None, + url: Optional[Text] = None, + **kwargs, + ): + self.status = status + self.data = data + self.details = details + self.completed = completed + self.error_message = error_message + self.used_credits = used_credits + self.run_time = run_time + self.usage = usage + self.url = url + self.additional_fields = kwargs + + def __getitem__(self, key: Text) -> Any: + if key in self.__dict__: + return self.__dict__[key] + elif self.additional_fields and key in self.additional_fields: + return self.additional_fields[key] + elif key == "usedCredits": + return self.used_credits + elif key == "runTime": + return self.run_time + raise KeyError(f"Key '{key}' not found in ModelResponse.") + + def get(self, key: Text) -> Any: + return self[key] + + def __repr__(self) -> str: + fields = [] + if self.status: + fields.append(f"status={self.status}") + if self.data: + fields.append(f"data='{self.data}'") + if self.details: + fields.append(f"details={self.details}") + if self.completed: + fields.append(f"completed={self.completed}") + if self.error_message: + fields.append(f"error_message='{self.error_message}'") + if self.used_credits: + fields.append(f"used_credits={self.used_credits}") + if self.run_time: + fields.append(f"run_time={self.run_time}") + if self.usage: + fields.append(f"usage={self.usage}") + if self.url: + fields.append(f"url='{self.url}'") + if self.additional_fields: + fields.extend([f"{k}={repr(v)}" for k, v in self.additional_fields.items()]) + return f"ModelResponse({', '.join(fields)})" + + def __contains__(self, key: Text) -> bool: + try: + self[key] + return True + except KeyError: + return False diff --git a/aixplain/modules/pipeline/asset.py b/aixplain/modules/pipeline/asset.py index 0e9ed56e..308f19b3 100644 --- a/aixplain/modules/pipeline/asset.py +++ b/aixplain/modules/pipeline/asset.py @@ -139,6 +139,11 @@ def poll(self, poll_url: Text, name: Text = "pipeline_process") -> Dict: r = _request_with_retry("get", poll_url, headers=headers) try: resp = r.json() + if "data" in resp and isinstance(resp["data"], str): + try: + resp["data"] = json.loads(resp["data"])["response"] + except Exception: + resp = r.json() logging.info(f"Single Poll for Pipeline: Status of polling for {name} : {resp}") except Exception: resp = {"status": "FAILED"} @@ -151,6 +156,7 @@ def run( name: Text = "pipeline_process", timeout: float = 20000.0, wait_time: float = 1.0, + batch_mode: bool = True, **kwargs, ) -> Dict: """Runs a pipeline call. @@ -161,6 +167,7 @@ def run( name (Text, optional): ID given to a call. Defaults to "pipeline_process". timeout (float, optional): total polling time. Defaults to 20000.0. wait_time (float, optional): wait time in seconds between polling calls. Defaults to 1.0. + batch_mode (bool, optional): Whether to run the pipeline in batch mode or online. Defaults to True. kwargs: A dictionary of keyword arguments. The keys are the argument names Returns: @@ -168,7 +175,7 @@ def run( """ start = time.time() try: - response = self.run_async(data, data_asset=data_asset, name=name, **kwargs) + response = self.run_async(data, data_asset=data_asset, name=name, batch_mode=batch_mode, **kwargs) if response["status"] == "FAILED": end = time.time() response["elapsed_time"] = end - start @@ -297,7 +304,12 @@ def __prepare_payload( return payload def run_async( - self, data: Union[Text, Dict], data_asset: Optional[Union[Text, Dict]] = None, name: Text = "pipeline_process", **kwargs + self, + data: Union[Text, Dict], + data_asset: Optional[Union[Text, Dict]] = None, + name: Text = "pipeline_process", + batch_mode: bool = True, + **kwargs, ) -> Dict: """Runs asynchronously a pipeline call. @@ -305,6 +317,7 @@ def run_async( data (Union[Text, Dict]): link to the input data data_asset (Optional[Union[Text, Dict]], optional): Data asset to be processed by the pipeline. Defaults to None. name (Text, optional): ID given to a call. Defaults to "pipeline_process". + batch_mode (bool, optional): Whether to run the pipeline in batch mode or online. Defaults to True. kwargs: A dictionary of keyword arguments. The keys are the argument names Returns: @@ -316,6 +329,7 @@ def run_async( } payload = self.__prepare_payload(data=data, data_asset=data_asset) + payload["batchmode"] = batch_mode payload.update(kwargs) payload = json.dumps(payload) call_url = f"{self.url}/{self.id}" diff --git a/aixplain/modules/pipeline/default.py b/aixplain/modules/pipeline/default.py index b0499159..41ae3c71 100644 --- a/aixplain/modules/pipeline/default.py +++ b/aixplain/modules/pipeline/default.py @@ -3,7 +3,6 @@ class DefaultPipeline(PipelineAsset, DesignerPipeline): - def __init__(self, *args, **kwargs): PipelineAsset.__init__(self, *args, **kwargs) DesignerPipeline.__init__(self) diff --git a/aixplain/modules/pipeline/designer/__init__.py b/aixplain/modules/pipeline/designer/__init__.py index 81571f21..6a493aa4 100644 --- a/aixplain/modules/pipeline/designer/__init__.py +++ b/aixplain/modules/pipeline/designer/__init__.py @@ -10,7 +10,7 @@ BaseSegmentor, BaseMetric, BareAsset, - BareMetric + BareMetric, ) from .pipeline import DesignerPipeline from .base import ( @@ -68,5 +68,5 @@ "TI", "TO", "BaseMetric", - "BareMetric" + "BareMetric", ] diff --git a/aixplain/modules/pipeline/designer/base.py b/aixplain/modules/pipeline/designer/base.py index 49c68463..08d4c8c5 100644 --- a/aixplain/modules/pipeline/designer/base.py +++ b/aixplain/modules/pipeline/designer/base.py @@ -1,3 +1,4 @@ +import re from typing import ( List, Union, @@ -11,7 +12,7 @@ from aixplain.enums import DataType from .enums import NodeType, ParamType - +from .utils import find_prompt_params if TYPE_CHECKING: from .pipeline import DesignerPipeline @@ -207,9 +208,7 @@ def validate(self): # Should we check for data type mismatch? if from_param.data_type and to_param.data_type: if from_param.data_type != to_param.data_type: - raise ValueError( - f"Data type mismatch between {from_param.data_type} and {to_param.data_type}" # noqa - ) + raise ValueError(f"Data type mismatch between {from_param.data_type} and {to_param.data_type}") # noqa def attach_to(self, pipeline: "DesignerPipeline"): """ @@ -260,9 +259,7 @@ def add_param(self, param: Param) -> None: if not hasattr(self, param.code): setattr(self, param.code, param) - def _create_param( - self, code: str, data_type: DataType = None, value: any = None - ) -> Param: + def _create_param(self, code: str, data_type: DataType = None, value: any = None) -> Param: raise NotImplementedError() def create_param( @@ -284,14 +281,31 @@ def __getitem__(self, code: str) -> Param: return param raise KeyError(f"Parameter with code '{code}' not found.") + def special_prompt_handling(self, code: str, value: str) -> None: + """ + This method will handle the special prompt handling for asset nodes + having `text-generation` function type. + """ + from .nodes import AssetNode + + if isinstance(self.node, AssetNode) and self.node.asset.function == "text-generation": + if code == "prompt": + matches = find_prompt_params(value) + for match in matches: + self.node.inputs.create_param(match, DataType.TEXT, is_required=True) + + def set_param_value(self, code: str, value: str) -> None: + self.special_prompt_handling(code, value) + self[code].value = value + def __setitem__(self, code: str, value: str) -> None: # set param value on set item to avoid setting it manually - self[code].value = value + self.set_param_value(code, value) def __setattr__(self, name: str, value: any) -> None: # set param value on attribute assignment to avoid setting it manually if isinstance(value, str) and hasattr(self, name): - self[name].value = value + self.set_param_value(name, value) else: super().__setattr__(name, value) @@ -326,9 +340,7 @@ def _create_param( class Outputs(ParamProxy): - def _create_param( - self, code: str, data_type: DataType = None, value: any = None - ) -> OutputParam: + def _create_param(self, code: str, data_type: DataType = None, value: any = None) -> OutputParam: return OutputParam(code=code, data_type=data_type, value=value) diff --git a/aixplain/modules/pipeline/designer/mixins.py b/aixplain/modules/pipeline/designer/mixins.py index e5aad3c4..44f653bf 100644 --- a/aixplain/modules/pipeline/designer/mixins.py +++ b/aixplain/modules/pipeline/designer/mixins.py @@ -53,9 +53,7 @@ def route(self, *params: Param) -> Node: """ assert self.pipeline, "Node not attached to a pipeline" - router = self.pipeline.router( - [(param.data_type, param.node) for param in params] - ) + router = self.pipeline.router([(param.data_type, param.node) for param in params]) self.outputs.input.link(router.inputs.input) for param in params: router.outputs.input.link(param) diff --git a/aixplain/modules/pipeline/designer/pipeline.py b/aixplain/modules/pipeline/designer/pipeline.py index d0522038..79013590 100644 --- a/aixplain/modules/pipeline/designer/pipeline.py +++ b/aixplain/modules/pipeline/designer/pipeline.py @@ -3,21 +3,10 @@ from aixplain.enums import DataType from .base import Serializable, Node, Link -from .nodes import ( - AssetNode, - Decision, - Script, - Input, - Output, - Router, - Route, - BareReconstructor, - BareSegmentor, - BareMetric -) +from .nodes import AssetNode, Decision, Script, Input, Output, Router, Route, BareReconstructor, BareSegmentor, BareMetric from .enums import NodeType, RouteType, Operation from .mixins import OutputableMixin - +from .utils import find_prompt_params T = TypeVar("T", bound="AssetNode") @@ -121,10 +110,7 @@ def is_param_linked(self, node, param): :return: True if the param is linked, False otherwise """ for link in self.links: - if ( - link.to_node.number == node.number - and param.code == link.to_param - ): + if link.to_node.number == node.number and param.code == link.to_param: return True return False @@ -139,6 +125,24 @@ def is_param_set(self, node, param): """ return param.value or self.is_param_linked(node, param) + def special_prompt_validation(self, node: Node): + """ + This method will handle the special rule for asset nodes having + `text-generation` function type where if any prompt variable exists + then the `text` param is not required but the prompt param are. + + :param node: the node + :raises ValueError: if the pipeline is not valid + """ + if isinstance(node, AssetNode) and node.asset.function == "text-generation": + if self.is_param_set(node, node.inputs.prompt): + matches = find_prompt_params(node.inputs.prompt.value) + if matches: + node.inputs.text.is_required = False + for match in matches: + if match not in node.inputs: + raise ValueError(f"Param {match} of node {node.label} should be defined and set") + def validate_params(self): """ This method will check if all required params are either set or linked @@ -146,11 +150,10 @@ def validate_params(self): :raises ValueError: if the pipeline is not valid """ for node in self.nodes: + self.special_prompt_validation(node) for param in node.inputs: if param.is_required and not self.is_param_set(node, param): - raise ValueError( - f"Param {param.code} of node {node.label} is required" - ) + raise ValueError(f"Param {param.code} of node {node.label} is required") def validate(self): """ @@ -176,11 +179,7 @@ def get_link(self, from_node: int, to_node: int) -> Link: :return: the link """ return next( - ( - link - for link in self.links - if link.from_node == from_node and link.to_node == to_node - ), + (link for link in self.links if link.from_node == from_node and link.to_node == to_node), None, ) @@ -192,9 +191,7 @@ def get_node(self, node_number: int) -> Node: :param node_number: the node number :return: the node """ - return next( - (node for node in self.nodes if node.number == node_number), None - ) + return next((node for node in self.nodes if node.number == node_number), None) def auto_infer(self): """ @@ -228,9 +225,7 @@ def infer_data_type(node): infer_data_type(self) infer_data_type(to_node) - def asset( - self, asset_id: str, *args, asset_class: Type[T] = AssetNode, **kwargs - ) -> T: + def asset(self, asset_id: str, *args, asset_class: Type[T] = AssetNode, **kwargs) -> T: """ Shortcut to create an asset node for the current pipeline. All params will be passed as keyword arguments to the node diff --git a/aixplain/modules/pipeline/designer/utils.py b/aixplain/modules/pipeline/designer/utils.py new file mode 100644 index 00000000..250d5501 --- /dev/null +++ b/aixplain/modules/pipeline/designer/utils.py @@ -0,0 +1,13 @@ +import re +from typing import List + + +def find_prompt_params(prompt: str) -> List[str]: + """ + This method will find the prompt parameters in the prompt string. + + :param prompt: the prompt string + :return: list of prompt parameters + """ + param_regex = re.compile(r"\{\{([^\}]+)\}\}") + return param_regex.findall(prompt) diff --git a/aixplain/modules/pipeline/generate.py b/aixplain/modules/pipeline/generate.py index a64917c1..46c95482 100644 --- a/aixplain/modules/pipeline/generate.py +++ b/aixplain/modules/pipeline/generate.py @@ -148,12 +148,7 @@ def populate_specs(functions: list): for function in functions: # slugify function name by trimming some special chars and # transforming it to snake case - function_name = ( - function["id"] - .replace("-", "_") - .replace("(", "_") - .replace(")", "_") - ) + function_name = function["id"].replace("-", "_").replace("(", "_").replace(")", "_") base_class = "AssetNode" is_segmentor = function["id"] in SEGMENTOR_FUNCTIONS is_reconstructor = function["id"] in RECONSTRUCTOR_FUNCTIONS @@ -170,9 +165,7 @@ def populate_specs(functions: list): "is_reconstructor": function["id"] in RECONSTRUCTOR_FUNCTIONS, "function_name": function_name, "base_class": base_class, - "class_name": "".join( - [w.title() for w in function_name.split("_")] - ), + "class_name": "".join([w.title() for w in function_name.split("_")]), "description": function["metaData"]["description"], "input_type": function["metaData"]["InputType"], "output_type": function["metaData"]["OutputType"], @@ -209,9 +202,7 @@ def populate_specs(functions: list): data_types = populate_data_types(functions) specs = populate_specs(functions) - print( - f"Populating module with {len(data_types)} data types and {len(specs)} specs" - ) + print(f"Populating module with {len(data_types)} data types and {len(specs)} specs") env = Environment( loader=BaseLoader(), trim_blocks=True, diff --git a/aixplain/modules/pipeline/pipeline.py b/aixplain/modules/pipeline/pipeline.py index e5675e4b..bf67ff15 100644 --- a/aixplain/modules/pipeline/pipeline.py +++ b/aixplain/modules/pipeline/pipeline.py @@ -4,18 +4,7 @@ from typing import Union, Type from aixplain.enums import DataType -from .designer import ( - InputParam, - OutputParam, - Inputs, - Outputs, - TI, - TO, - AssetNode, - BaseReconstructor, - BaseSegmentor, - BaseMetric -) +from .designer import InputParam, OutputParam, Inputs, Outputs, TI, TO, AssetNode, BaseReconstructor, BaseSegmentor, BaseMetric from .default import DefaultPipeline from aixplain.modules import asset @@ -38,13 +27,14 @@ def __init__(self, node=None): class ObjectDetection(AssetNode[ObjectDetectionInputs, ObjectDetectionOutputs]): """ - Object Detection is a computer vision technology that identifies and locates -objects within an image, typically by drawing bounding boxes around the -detected objects and classifying them into predefined categories. + Object Detection is a computer vision technology that identifies and locates + objects within an image, typically by drawing bounding boxes around the + detected objects and classifying them into predefined categories. - InputType: video - OutputType: text + InputType: video + OutputType: text """ + function: str = "object-detection" input_type: str = DataType.VIDEO output_type: str = DataType.TEXT @@ -71,12 +61,13 @@ def __init__(self, node=None): class LanguageIdentification(AssetNode[LanguageIdentificationInputs, LanguageIdentificationOutputs]): """ - Language Identification is the process of automatically determining the -language in which a given piece of text is written. + Language Identification is the process of automatically determining the + language in which a given piece of text is written. - InputType: text - OutputType: text + InputType: text + OutputType: text """ + function: str = "language-identification" input_type: str = DataType.TEXT output_type: str = DataType.TEXT @@ -105,14 +96,15 @@ def __init__(self, node=None): class Ocr(AssetNode[OcrInputs, OcrOutputs]): """ - OCR, or Optical Character Recognition, is a technology that converts different -types of documents, such as scanned paper documents, PDFs, or images captured -by a digital camera, into editable and searchable data by recognizing and -extracting text from the images. + OCR, or Optical Character Recognition, is a technology that converts different + types of documents, such as scanned paper documents, PDFs, or images captured + by a digital camera, into editable and searchable data by recognizing and + extracting text from the images. - InputType: image - OutputType: text + InputType: image + OutputType: text """ + function: str = "ocr" input_type: str = DataType.IMAGE output_type: str = DataType.TEXT @@ -139,13 +131,14 @@ def __init__(self, node=None): class ScriptExecution(AssetNode[ScriptExecutionInputs, ScriptExecutionOutputs]): """ - Script Execution refers to the process of running a set of programmed -instructions or code within a computing environment, enabling the automated -performance of tasks, calculations, or operations as defined by the script. + Script Execution refers to the process of running a set of programmed + instructions or code within a computing environment, enabling the automated + performance of tasks, calculations, or operations as defined by the script. - InputType: text - OutputType: text + InputType: text + OutputType: text """ + function: str = "script-execution" input_type: str = DataType.TEXT output_type: str = DataType.TEXT @@ -174,13 +167,14 @@ def __init__(self, node=None): class ImageLabelDetection(AssetNode[ImageLabelDetectionInputs, ImageLabelDetectionOutputs]): """ - Image Label Detection is a function that automatically identifies and assigns -descriptive tags or labels to objects, scenes, or elements within an image, -enabling easier categorization, search, and analysis of visual content. + Image Label Detection is a function that automatically identifies and assigns + descriptive tags or labels to objects, scenes, or elements within an image, + enabling easier categorization, search, and analysis of visual content. - InputType: image - OutputType: label + InputType: image + OutputType: label """ + function: str = "image-label-detection" input_type: str = DataType.IMAGE output_type: str = DataType.LABEL @@ -207,14 +201,15 @@ def __init__(self, node=None): class ImageCaptioning(AssetNode[ImageCaptioningInputs, ImageCaptioningOutputs]): """ - Image Captioning is a process that involves generating a textual description of -an image, typically using machine learning models to analyze the visual content -and produce coherent and contextually relevant sentences that describe the -objects, actions, and scenes depicted in the image. + Image Captioning is a process that involves generating a textual description of + an image, typically using machine learning models to analyze the visual content + and produce coherent and contextually relevant sentences that describe the + objects, actions, and scenes depicted in the image. - InputType: image - OutputType: text + InputType: image + OutputType: text """ + function: str = "image-captioning" input_type: str = DataType.IMAGE output_type: str = DataType.TEXT @@ -241,12 +236,13 @@ def __init__(self, node=None): class AudioLanguageIdentification(AssetNode[AudioLanguageIdentificationInputs, AudioLanguageIdentificationOutputs]): """ - Audio Language Identification is a process that involves analyzing an audio -recording to determine the language being spoken. + Audio Language Identification is a process that involves analyzing an audio + recording to determine the language being spoken. - InputType: audio - OutputType: label + InputType: audio + OutputType: label """ + function: str = "audio-language-identification" input_type: str = DataType.AUDIO output_type: str = DataType.LABEL @@ -273,13 +269,14 @@ def __init__(self, node=None): class AsrAgeClassification(AssetNode[AsrAgeClassificationInputs, AsrAgeClassificationOutputs]): """ - The ASR Age Classification function is designed to analyze audio recordings of -speech to determine the speaker's age group by leveraging automatic speech -recognition (ASR) technology and machine learning algorithms. + The ASR Age Classification function is designed to analyze audio recordings of + speech to determine the speaker's age group by leveraging automatic speech + recognition (ASR) technology and machine learning algorithms. - InputType: audio - OutputType: label + InputType: audio + OutputType: label """ + function: str = "asr-age-classification" input_type: str = DataType.AUDIO output_type: str = DataType.LABEL @@ -310,13 +307,14 @@ def __init__(self, node=None): class BenchmarkScoringMt(AssetNode[BenchmarkScoringMtInputs, BenchmarkScoringMtOutputs]): """ - Benchmark Scoring MT is a function designed to evaluate and score machine -translation systems by comparing their output against a set of predefined -benchmarks, thereby assessing their accuracy and performance. + Benchmark Scoring MT is a function designed to evaluate and score machine + translation systems by comparing their output against a set of predefined + benchmarks, thereby assessing their accuracy and performance. - InputType: text - OutputType: label + InputType: text + OutputType: label """ + function: str = "benchmark-scoring-mt" input_type: str = DataType.TEXT output_type: str = DataType.LABEL @@ -343,12 +341,13 @@ def __init__(self, node=None): class AsrGenderClassification(AssetNode[AsrGenderClassificationInputs, AsrGenderClassificationOutputs]): """ - The ASR Gender Classification function analyzes audio recordings to determine -and classify the speaker's gender based on their voice characteristics. + The ASR Gender Classification function analyzes audio recordings to determine + and classify the speaker's gender based on their voice characteristics. - InputType: audio - OutputType: label + InputType: audio + OutputType: label """ + function: str = "asr-gender-classification" input_type: str = DataType.AUDIO output_type: str = DataType.LABEL @@ -377,13 +376,14 @@ def __init__(self, node=None): class BaseModel(AssetNode[BaseModelInputs, BaseModelOutputs]): """ - The Base-Model function serves as a foundational framework designed to provide -essential features and capabilities upon which more specialized or advanced -models can be built and customized. + The Base-Model function serves as a foundational framework designed to provide + essential features and capabilities upon which more specialized or advanced + models can be built and customized. - InputType: text - OutputType: text + InputType: text + OutputType: text """ + function: str = "base-model" input_type: str = DataType.TEXT output_type: str = DataType.TEXT @@ -410,12 +410,13 @@ def __init__(self, node=None): class LanguageIdentificationAudio(AssetNode[LanguageIdentificationAudioInputs, LanguageIdentificationAudioOutputs]): """ - The Language Identification Audio function analyzes audio input to determine -and identify the language being spoken. + The Language Identification Audio function analyzes audio input to determine + and identify the language being spoken. - InputType: audio - OutputType: label + InputType: audio + OutputType: label """ + function: str = "language-identification-audio" input_type: str = DataType.AUDIO output_type: str = DataType.LABEL @@ -442,14 +443,15 @@ def __init__(self, node=None): class Loglikelihood(AssetNode[LoglikelihoodInputs, LoglikelihoodOutputs]): """ - The Log Likelihood function measures the probability of observing the given -data under a specific statistical model by taking the natural logarithm of the -likelihood function, thereby transforming the product of probabilities into a -sum, which simplifies the process of optimization and parameter estimation. + The Log Likelihood function measures the probability of observing the given + data under a specific statistical model by taking the natural logarithm of the + likelihood function, thereby transforming the product of probabilities into a + sum, which simplifies the process of optimization and parameter estimation. - InputType: text - OutputType: number + InputType: text + OutputType: number """ + function: str = "loglikelihood" input_type: str = DataType.TEXT output_type: str = DataType.NUMBER @@ -478,13 +480,14 @@ def __init__(self, node=None): class VideoEmbedding(AssetNode[VideoEmbeddingInputs, VideoEmbeddingOutputs]): """ - Video Embedding is a process that transforms video content into a fixed- -dimensional vector representation, capturing essential features and patterns to -facilitate tasks such as retrieval, classification, and recommendation. + Video Embedding is a process that transforms video content into a fixed- + dimensional vector representation, capturing essential features and patterns to + facilitate tasks such as retrieval, classification, and recommendation. - InputType: video - OutputType: embedding + InputType: video + OutputType: embedding """ + function: str = "video-embedding" input_type: str = DataType.VIDEO output_type: str = DataType.EMBEDDING @@ -513,13 +516,14 @@ def __init__(self, node=None): class TextSegmenation(AssetNode[TextSegmenationInputs, TextSegmenationOutputs]): """ - Text Segmentation is the process of dividing a continuous text into meaningful -units, such as words, sentences, or topics, to facilitate easier analysis and -understanding. + Text Segmentation is the process of dividing a continuous text into meaningful + units, such as words, sentences, or topics, to facilitate easier analysis and + understanding. - InputType: text - OutputType: text + InputType: text + OutputType: text """ + function: str = "text-segmenation" input_type: str = DataType.TEXT output_type: str = DataType.TEXT @@ -548,14 +552,15 @@ def __init__(self, node=None): class ImageEmbedding(AssetNode[ImageEmbeddingInputs, ImageEmbeddingOutputs]): """ - Image Embedding is a process that transforms an image into a fixed-dimensional -vector representation, capturing its essential features and enabling efficient -comparison, retrieval, and analysis in various machine learning and computer -vision tasks. + Image Embedding is a process that transforms an image into a fixed-dimensional + vector representation, capturing its essential features and enabling efficient + comparison, retrieval, and analysis in various machine learning and computer + vision tasks. - InputType: image - OutputType: text + InputType: image + OutputType: text """ + function: str = "image-embedding" input_type: str = DataType.IMAGE output_type: str = DataType.TEXT @@ -584,13 +589,14 @@ def __init__(self, node=None): class ImageManipulation(AssetNode[ImageManipulationInputs, ImageManipulationOutputs]): """ - Image Manipulation refers to the process of altering or enhancing digital -images using various techniques and tools to achieve desired visual effects, -correct imperfections, or transform the image's appearance. + Image Manipulation refers to the process of altering or enhancing digital + images using various techniques and tools to achieve desired visual effects, + correct imperfections, or transform the image's appearance. - InputType: image - OutputType: image + InputType: image + OutputType: image """ + function: str = "image-manipulation" input_type: str = DataType.IMAGE output_type: str = DataType.IMAGE @@ -619,14 +625,15 @@ def __init__(self, node=None): class ImageToVideoGeneration(AssetNode[ImageToVideoGenerationInputs, ImageToVideoGenerationOutputs]): """ - The Image To Video Generation function transforms a series of static images -into a cohesive, dynamic video sequence, often incorporating transitions, -effects, and synchronization with audio to create a visually engaging -narrative. + The Image To Video Generation function transforms a series of static images + into a cohesive, dynamic video sequence, often incorporating transitions, + effects, and synchronization with audio to create a visually engaging + narrative. - InputType: image - OutputType: video + InputType: image + OutputType: video """ + function: str = "image-to-video-generation" input_type: str = DataType.IMAGE output_type: str = DataType.VIDEO @@ -663,13 +670,14 @@ def __init__(self, node=None): class AudioForcedAlignment(AssetNode[AudioForcedAlignmentInputs, AudioForcedAlignmentOutputs]): """ - Audio Forced Alignment is a process that synchronizes a given audio recording -with its corresponding transcript by precisely aligning each spoken word or -phoneme to its exact timing within the audio. + Audio Forced Alignment is a process that synchronizes a given audio recording + with its corresponding transcript by precisely aligning each spoken word or + phoneme to its exact timing within the audio. - InputType: audio - OutputType: audio + InputType: audio + OutputType: audio """ + function: str = "audio-forced-alignment" input_type: str = DataType.AUDIO output_type: str = DataType.AUDIO @@ -700,13 +708,14 @@ def __init__(self, node=None): class BenchmarkScoringAsr(AssetNode[BenchmarkScoringAsrInputs, BenchmarkScoringAsrOutputs]): """ - Benchmark Scoring ASR is a function that evaluates and compares the performance -of automatic speech recognition systems by analyzing their accuracy, speed, and -other relevant metrics against a standardized set of benchmarks. + Benchmark Scoring ASR is a function that evaluates and compares the performance + of automatic speech recognition systems by analyzing their accuracy, speed, and + other relevant metrics against a standardized set of benchmarks. - InputType: audio - OutputType: label + InputType: audio + OutputType: label """ + function: str = "benchmark-scoring-asr" input_type: str = DataType.AUDIO output_type: str = DataType.LABEL @@ -737,13 +746,14 @@ def __init__(self, node=None): class VisualQuestionAnswering(AssetNode[VisualQuestionAnsweringInputs, VisualQuestionAnsweringOutputs]): """ - Visual Question Answering (VQA) is a task in artificial intelligence that -involves analyzing an image and providing accurate, contextually relevant -answers to questions posed about the visual content of that image. + Visual Question Answering (VQA) is a task in artificial intelligence that + involves analyzing an image and providing accurate, contextually relevant + answers to questions posed about the visual content of that image. - InputType: image - OutputType: video + InputType: image + OutputType: video """ + function: str = "visual-question-answering" input_type: str = DataType.IMAGE output_type: str = DataType.VIDEO @@ -770,13 +780,14 @@ def __init__(self, node=None): class DocumentImageParsing(AssetNode[DocumentImageParsingInputs, DocumentImageParsingOutputs]): """ - Document Image Parsing is the process of analyzing and converting scanned or -photographed images of documents into structured, machine-readable formats by -identifying and extracting text, layout, and other relevant information. + Document Image Parsing is the process of analyzing and converting scanned or + photographed images of documents into structured, machine-readable formats by + identifying and extracting text, layout, and other relevant information. - InputType: image - OutputType: text + InputType: image + OutputType: text """ + function: str = "document-image-parsing" input_type: str = DataType.IMAGE output_type: str = DataType.TEXT @@ -803,14 +814,15 @@ def __init__(self, node=None): class DocumentInformationExtraction(AssetNode[DocumentInformationExtractionInputs, DocumentInformationExtractionOutputs]): """ - Document Information Extraction is the process of automatically identifying, -extracting, and structuring relevant data from unstructured or semi-structured -documents, such as invoices, receipts, contracts, and forms, to facilitate -easier data management and analysis. + Document Information Extraction is the process of automatically identifying, + extracting, and structuring relevant data from unstructured or semi-structured + documents, such as invoices, receipts, contracts, and forms, to facilitate + easier data management and analysis. - InputType: image - OutputType: text + InputType: image + OutputType: text """ + function: str = "document-information-extraction" input_type: str = DataType.IMAGE output_type: str = DataType.TEXT @@ -839,13 +851,14 @@ def __init__(self, node=None): class DepthEstimation(AssetNode[DepthEstimationInputs, DepthEstimationOutputs]): """ - Depth estimation is a computational process that determines the distance of -objects from a viewpoint, typically using visual data from cameras or sensors -to create a three-dimensional understanding of a scene. + Depth estimation is a computational process that determines the distance of + objects from a viewpoint, typically using visual data from cameras or sensors + to create a three-dimensional understanding of a scene. - InputType: image - OutputType: text + InputType: image + OutputType: text """ + function: str = "depth-estimation" input_type: str = DataType.IMAGE output_type: str = DataType.TEXT @@ -872,14 +885,15 @@ def __init__(self, node=None): class VideoGeneration(AssetNode[VideoGenerationInputs, VideoGenerationOutputs]): """ - Video Generation is the process of creating video content through automated or -semi-automated means, often utilizing algorithms, artificial intelligence, or -software tools to produce visual and audio elements that can range from simple -animations to complex, realistic scenes. + Video Generation is the process of creating video content through automated or + semi-automated means, often utilizing algorithms, artificial intelligence, or + software tools to produce visual and audio elements that can range from simple + animations to complex, realistic scenes. - InputType: text - OutputType: video + InputType: text + OutputType: video """ + function: str = "video-generation" input_type: str = DataType.TEXT output_type: str = DataType.VIDEO @@ -908,15 +922,18 @@ def __init__(self, node=None): self.data = self.create_param(code="data", data_type=DataType.TEXT) -class ReferencelessAudioGenerationMetric(BaseMetric[ReferencelessAudioGenerationMetricInputs, ReferencelessAudioGenerationMetricOutputs]): +class ReferencelessAudioGenerationMetric( + BaseMetric[ReferencelessAudioGenerationMetricInputs, ReferencelessAudioGenerationMetricOutputs] +): """ - The Referenceless Audio Generation Metric is a tool designed to evaluate the -quality of generated audio content without the need for a reference or original -audio sample for comparison. + The Referenceless Audio Generation Metric is a tool designed to evaluate the + quality of generated audio content without the need for a reference or original + audio sample for comparison. - InputType: text - OutputType: text + InputType: text + OutputType: text """ + function: str = "referenceless-audio-generation-metric" input_type: str = DataType.TEXT output_type: str = DataType.TEXT @@ -943,13 +960,14 @@ def __init__(self, node=None): class MultiClassImageClassification(AssetNode[MultiClassImageClassificationInputs, MultiClassImageClassificationOutputs]): """ - Multi Class Image Classification is a machine learning task where an algorithm -is trained to categorize images into one of several predefined classes or -categories based on their visual content. + Multi Class Image Classification is a machine learning task where an algorithm + is trained to categorize images into one of several predefined classes or + categories based on their visual content. - InputType: image - OutputType: label + InputType: image + OutputType: label """ + function: str = "multi-class-image-classification" input_type: str = DataType.IMAGE output_type: str = DataType.LABEL @@ -976,13 +994,14 @@ def __init__(self, node=None): class SemanticSegmentation(AssetNode[SemanticSegmentationInputs, SemanticSegmentationOutputs]): """ - Semantic segmentation is a computer vision process that involves classifying -each pixel in an image into a predefined category, effectively partitioning the -image into meaningful segments based on the objects or regions they represent. + Semantic segmentation is a computer vision process that involves classifying + each pixel in an image into a predefined category, effectively partitioning the + image into meaningful segments based on the objects or regions they represent. - InputType: image - OutputType: label + InputType: image + OutputType: label """ + function: str = "semantic-segmentation" input_type: str = DataType.IMAGE output_type: str = DataType.LABEL @@ -1009,14 +1028,15 @@ def __init__(self, node=None): class InstanceSegmentation(AssetNode[InstanceSegmentationInputs, InstanceSegmentationOutputs]): """ - Instance segmentation is a computer vision task that involves detecting and -delineating each distinct object within an image, assigning a unique label and -precise boundary to every individual instance of objects, even if they belong -to the same category. + Instance segmentation is a computer vision task that involves detecting and + delineating each distinct object within an image, assigning a unique label and + precise boundary to every individual instance of objects, even if they belong + to the same category. - InputType: image - OutputType: label + InputType: image + OutputType: label """ + function: str = "instance-segmentation" input_type: str = DataType.IMAGE output_type: str = DataType.LABEL @@ -1043,14 +1063,15 @@ def __init__(self, node=None): class ImageColorization(AssetNode[ImageColorizationInputs, ImageColorizationOutputs]): """ - Image colorization is a process that involves adding color to grayscale images, -transforming them from black-and-white to full-color representations, often -using advanced algorithms and machine learning techniques to predict and apply -the appropriate hues and shades. + Image colorization is a process that involves adding color to grayscale images, + transforming them from black-and-white to full-color representations, often + using advanced algorithms and machine learning techniques to predict and apply + the appropriate hues and shades. - InputType: image - OutputType: image + InputType: image + OutputType: image """ + function: str = "image-colorization" input_type: str = DataType.IMAGE output_type: str = DataType.IMAGE @@ -1083,14 +1104,15 @@ def __init__(self, node=None): class AudioGenerationMetric(BaseMetric[AudioGenerationMetricInputs, AudioGenerationMetricOutputs]): """ - The Audio Generation Metric is a quantitative measure used to evaluate the -quality, accuracy, and overall performance of audio generated by artificial -intelligence systems, often considering factors such as fidelity, -intelligibility, and similarity to human-produced audio. + The Audio Generation Metric is a quantitative measure used to evaluate the + quality, accuracy, and overall performance of audio generated by artificial + intelligence systems, often considering factors such as fidelity, + intelligibility, and similarity to human-produced audio. - InputType: text - OutputType: text + InputType: text + OutputType: text """ + function: str = "audio-generation-metric" input_type: str = DataType.TEXT output_type: str = DataType.TEXT @@ -1117,14 +1139,15 @@ def __init__(self, node=None): class ImageImpainting(AssetNode[ImageImpaintingInputs, ImageImpaintingOutputs]): """ - Image inpainting is a process that involves filling in missing or damaged parts -of an image in a way that is visually coherent and seamlessly blends with the -surrounding areas, often using advanced algorithms and techniques to restore -the image to its original or intended appearance. + Image inpainting is a process that involves filling in missing or damaged parts + of an image in a way that is visually coherent and seamlessly blends with the + surrounding areas, often using advanced algorithms and techniques to restore + the image to its original or intended appearance. - InputType: image - OutputType: image + InputType: image + OutputType: image """ + function: str = "image-impainting" input_type: str = DataType.IMAGE output_type: str = DataType.IMAGE @@ -1151,14 +1174,15 @@ def __init__(self, node=None): class StyleTransfer(AssetNode[StyleTransferInputs, StyleTransferOutputs]): """ - Style Transfer is a technique in artificial intelligence that applies the -visual style of one image (such as the brushstrokes of a famous painting) to -the content of another image, effectively blending the artistic elements of the -first image with the subject matter of the second. + Style Transfer is a technique in artificial intelligence that applies the + visual style of one image (such as the brushstrokes of a famous painting) to + the content of another image, effectively blending the artistic elements of the + first image with the subject matter of the second. - InputType: image - OutputType: image + InputType: image + OutputType: image """ + function: str = "style-transfer" input_type: str = DataType.IMAGE output_type: str = DataType.IMAGE @@ -1187,13 +1211,14 @@ def __init__(self, node=None): class MultiClassTextClassification(AssetNode[MultiClassTextClassificationInputs, MultiClassTextClassificationOutputs]): """ - Multi Class Text Classification is a natural language processing task that -involves categorizing a given text into one of several predefined classes or -categories based on its content. + Multi Class Text Classification is a natural language processing task that + involves categorizing a given text into one of several predefined classes or + categories based on its content. - InputType: text - OutputType: label + InputType: text + OutputType: label """ + function: str = "multi-class-text-classification" input_type: str = DataType.TEXT output_type: str = DataType.LABEL @@ -1226,14 +1251,15 @@ def __init__(self, node=None): class TextEmbedding(AssetNode[TextEmbeddingInputs, TextEmbeddingOutputs]): """ - Text embedding is a process that converts text into numerical vectors, -capturing the semantic meaning and contextual relationships of words or -phrases, enabling machines to understand and analyze natural language more -effectively. + Text embedding is a process that converts text into numerical vectors, + capturing the semantic meaning and contextual relationships of words or + phrases, enabling machines to understand and analyze natural language more + effectively. - InputType: text - OutputType: text + InputType: text + OutputType: text """ + function: str = "text-embedding" input_type: str = DataType.TEXT output_type: str = DataType.TEXT @@ -1262,14 +1288,15 @@ def __init__(self, node=None): class MultiLabelTextClassification(AssetNode[MultiLabelTextClassificationInputs, MultiLabelTextClassificationOutputs]): """ - Multi Label Text Classification is a natural language processing task where a -given text is analyzed and assigned multiple relevant labels or categories from -a predefined set, allowing for the text to belong to more than one category -simultaneously. + Multi Label Text Classification is a natural language processing task where a + given text is analyzed and assigned multiple relevant labels or categories from + a predefined set, allowing for the text to belong to more than one category + simultaneously. - InputType: text - OutputType: label + InputType: text + OutputType: label """ + function: str = "multi-label-text-classification" input_type: str = DataType.TEXT output_type: str = DataType.LABEL @@ -1296,12 +1323,13 @@ def __init__(self, node=None): class TextReconstruction(BaseReconstructor[TextReconstructionInputs, TextReconstructionOutputs]): """ - Text Reconstruction is a process that involves piecing together fragmented or -incomplete text data to restore it to its original, coherent form. + Text Reconstruction is a process that involves piecing together fragmented or + incomplete text data to restore it to its original, coherent form. - InputType: text - OutputType: text + InputType: text + OutputType: text """ + function: str = "text-reconstruction" input_type: str = DataType.TEXT output_type: str = DataType.TEXT @@ -1330,13 +1358,14 @@ def __init__(self, node=None): class FactChecking(AssetNode[FactCheckingInputs, FactCheckingOutputs]): """ - Fact Checking is the process of verifying the accuracy and truthfulness of -information, statements, or claims by cross-referencing with reliable sources -and evidence. + Fact Checking is the process of verifying the accuracy and truthfulness of + information, statements, or claims by cross-referencing with reliable sources + and evidence. - InputType: text - OutputType: label + InputType: text + OutputType: label """ + function: str = "fact-checking" input_type: str = DataType.TEXT output_type: str = DataType.LABEL @@ -1369,13 +1398,14 @@ def __init__(self, node=None): class SpeechClassification(AssetNode[SpeechClassificationInputs, SpeechClassificationOutputs]): """ - Speech Classification is a process that involves analyzing and categorizing -spoken language into predefined categories or classes based on various features -such as tone, pitch, and linguistic content. + Speech Classification is a process that involves analyzing and categorizing + spoken language into predefined categories or classes based on various features + such as tone, pitch, and linguistic content. - InputType: audio - OutputType: label + InputType: audio + OutputType: label """ + function: str = "speech-classification" input_type: str = DataType.AUDIO output_type: str = DataType.LABEL @@ -1404,14 +1434,15 @@ def __init__(self, node=None): class IntentClassification(AssetNode[IntentClassificationInputs, IntentClassificationOutputs]): """ - Intent Classification is a natural language processing task that involves -analyzing and categorizing user text input to determine the underlying purpose -or goal behind the communication, such as booking a flight, asking for weather -information, or setting a reminder. + Intent Classification is a natural language processing task that involves + analyzing and categorizing user text input to determine the underlying purpose + or goal behind the communication, such as booking a flight, asking for weather + information, or setting a reminder. - InputType: text - OutputType: label + InputType: text + OutputType: label """ + function: str = "intent-classification" input_type: str = DataType.TEXT output_type: str = DataType.LABEL @@ -1440,14 +1471,15 @@ def __init__(self, node=None): class PartOfSpeechTagging(AssetNode[PartOfSpeechTaggingInputs, PartOfSpeechTaggingOutputs]): """ - Part of Speech Tagging is a natural language processing task that involves -assigning each word in a sentence its corresponding part of speech, such as -noun, verb, adjective, or adverb, based on its role and context within the -sentence. + Part of Speech Tagging is a natural language processing task that involves + assigning each word in a sentence its corresponding part of speech, such as + noun, verb, adjective, or adverb, based on its role and context within the + sentence. - InputType: text - OutputType: label + InputType: text + OutputType: label """ + function: str = "part-of-speech-tagging" input_type: str = DataType.TEXT output_type: str = DataType.LABEL @@ -1474,14 +1506,15 @@ def __init__(self, node=None): class MetricAggregation(BaseMetric[MetricAggregationInputs, MetricAggregationOutputs]): """ - Metric Aggregation is a function that computes and summarizes numerical data by -applying statistical operations, such as averaging, summing, or finding the -minimum and maximum values, to provide insights and facilitate analysis of -large datasets. + Metric Aggregation is a function that computes and summarizes numerical data by + applying statistical operations, such as averaging, summing, or finding the + minimum and maximum values, to provide insights and facilitate analysis of + large datasets. - InputType: text - OutputType: text + InputType: text + OutputType: text """ + function: str = "metric-aggregation" input_type: str = DataType.TEXT output_type: str = DataType.TEXT @@ -1510,14 +1543,15 @@ def __init__(self, node=None): class DialectDetection(AssetNode[DialectDetectionInputs, DialectDetectionOutputs]): """ - Dialect Detection is a function that identifies and classifies the specific -regional or social variations of a language spoken or written by an individual, -enabling the recognition of distinct linguistic patterns and nuances associated -with different dialects. + Dialect Detection is a function that identifies and classifies the specific + regional or social variations of a language spoken or written by an individual, + enabling the recognition of distinct linguistic patterns and nuances associated + with different dialects. - InputType: audio - OutputType: text + InputType: audio + OutputType: text """ + function: str = "dialect-detection" input_type: str = DataType.AUDIO output_type: str = DataType.TEXT @@ -1544,13 +1578,14 @@ def __init__(self, node=None): class InverseTextNormalization(AssetNode[InverseTextNormalizationInputs, InverseTextNormalizationOutputs]): """ - Inverse Text Normalization is the process of converting spoken or written -language in its normalized form, such as numbers, dates, and abbreviations, -back into their original, more complex or detailed textual representations. + Inverse Text Normalization is the process of converting spoken or written + language in its normalized form, such as numbers, dates, and abbreviations, + back into their original, more complex or detailed textual representations. - InputType: text - OutputType: label + InputType: text + OutputType: label """ + function: str = "inverse-text-normalization" input_type: str = DataType.TEXT output_type: str = DataType.LABEL @@ -1579,12 +1614,13 @@ def __init__(self, node=None): class TextToAudio(AssetNode[TextToAudioInputs, TextToAudioOutputs]): """ - The Text to Audio function converts written text into spoken words, allowing -users to listen to the content instead of reading it. + The Text to Audio function converts written text into spoken words, allowing + users to listen to the content instead of reading it. - InputType: text - OutputType: audio + InputType: text + OutputType: audio """ + function: str = "text-to-audio" input_type: str = DataType.TEXT output_type: str = DataType.AUDIO @@ -1617,13 +1653,14 @@ def __init__(self, node=None): class FillTextMask(AssetNode[FillTextMaskInputs, FillTextMaskOutputs]): """ - The "Fill Text Mask" function takes a text input with masked or placeholder -characters and replaces those placeholders with specified or contextually -appropriate characters to generate a complete and coherent text output. + The "Fill Text Mask" function takes a text input with masked or placeholder + characters and replaces those placeholders with specified or contextually + appropriate characters to generate a complete and coherent text output. - InputType: text - OutputType: text + InputType: text + OutputType: text """ + function: str = "fill-text-mask" input_type: str = DataType.TEXT output_type: str = DataType.TEXT @@ -1652,14 +1689,15 @@ def __init__(self, node=None): class VideoContentModeration(AssetNode[VideoContentModerationInputs, VideoContentModerationOutputs]): """ - Video Content Moderation is the process of reviewing, analyzing, and filtering -video content to ensure it adheres to community guidelines, legal standards, -and platform policies, thereby preventing the dissemination of inappropriate, -harmful, or illegal material. + Video Content Moderation is the process of reviewing, analyzing, and filtering + video content to ensure it adheres to community guidelines, legal standards, + and platform policies, thereby preventing the dissemination of inappropriate, + harmful, or illegal material. - InputType: video - OutputType: label + InputType: video + OutputType: label """ + function: str = "video-content-moderation" input_type: str = DataType.VIDEO output_type: str = DataType.LABEL @@ -1686,13 +1724,14 @@ def __init__(self, node=None): class ExtractAudioFromVideo(AssetNode[ExtractAudioFromVideoInputs, ExtractAudioFromVideoOutputs]): """ - The "Extract Audio From Video" function allows users to separate and save the -audio track from a video file, enabling them to obtain just the sound without -the accompanying visual content. + The "Extract Audio From Video" function allows users to separate and save the + audio track from a video file, enabling them to obtain just the sound without + the accompanying visual content. - InputType: video - OutputType: audio + InputType: video + OutputType: audio """ + function: str = "extract-audio-from-video" input_type: str = DataType.VIDEO output_type: str = DataType.AUDIO @@ -1721,13 +1760,14 @@ def __init__(self, node=None): class ImageCompression(AssetNode[ImageCompressionInputs, ImageCompressionOutputs]): """ - Image compression is a process that reduces the file size of an image by -removing redundant or non-essential data, while maintaining an acceptable level -of visual quality. + Image compression is a process that reduces the file size of an image by + removing redundant or non-essential data, while maintaining an acceptable level + of visual quality. - InputType: image - OutputType: image + InputType: image + OutputType: image """ + function: str = "image-compression" input_type: str = DataType.IMAGE output_type: str = DataType.IMAGE @@ -1756,13 +1796,14 @@ def __init__(self, node=None): class MultilingualSpeechRecognition(AssetNode[MultilingualSpeechRecognitionInputs, MultilingualSpeechRecognitionOutputs]): """ - Multilingual Speech Recognition is a technology that enables the automatic -transcription of spoken language into text across multiple languages, allowing -for seamless communication and understanding in diverse linguistic contexts. + Multilingual Speech Recognition is a technology that enables the automatic + transcription of spoken language into text across multiple languages, allowing + for seamless communication and understanding in diverse linguistic contexts. - InputType: audio - OutputType: text + InputType: audio + OutputType: text """ + function: str = "multilingual-speech-recognition" input_type: str = DataType.AUDIO output_type: str = DataType.TEXT @@ -1791,16 +1832,19 @@ def __init__(self, node=None): self.data = self.create_param(code="data", data_type=DataType.TEXT) -class ReferencelessTextGenerationMetric(BaseMetric[ReferencelessTextGenerationMetricInputs, ReferencelessTextGenerationMetricOutputs]): +class ReferencelessTextGenerationMetric( + BaseMetric[ReferencelessTextGenerationMetricInputs, ReferencelessTextGenerationMetricOutputs] +): """ - The Referenceless Text Generation Metric is a method for evaluating the quality -of generated text without requiring a reference text for comparison, often -leveraging models or algorithms to assess coherence, relevance, and fluency -based on intrinsic properties of the text itself. + The Referenceless Text Generation Metric is a method for evaluating the quality + of generated text without requiring a reference text for comparison, often + leveraging models or algorithms to assess coherence, relevance, and fluency + based on intrinsic properties of the text itself. - InputType: text - OutputType: text + InputType: text + OutputType: text """ + function: str = "referenceless-text-generation-metric" input_type: str = DataType.TEXT output_type: str = DataType.TEXT @@ -1833,13 +1877,14 @@ def __init__(self, node=None): class TextGenerationMetricDefault(BaseMetric[TextGenerationMetricDefaultInputs, TextGenerationMetricDefaultOutputs]): """ - The "Text Generation Metric Default" function provides a standard set of -evaluation metrics for assessing the quality and performance of text generation -models. + The "Text Generation Metric Default" function provides a standard set of + evaluation metrics for assessing the quality and performance of text generation + models. - InputType: text - OutputType: text + InputType: text + OutputType: text """ + function: str = "text-generation-metric-default" input_type: str = DataType.TEXT output_type: str = DataType.TEXT @@ -1866,13 +1911,14 @@ def __init__(self, node=None): class NoiseRemoval(AssetNode[NoiseRemovalInputs, NoiseRemovalOutputs]): """ - Noise Removal is a process that involves identifying and eliminating unwanted -random variations or disturbances from an audio signal to enhance the clarity -and quality of the underlying information. + Noise Removal is a process that involves identifying and eliminating unwanted + random variations or disturbances from an audio signal to enhance the clarity + and quality of the underlying information. - InputType: audio - OutputType: audio + InputType: audio + OutputType: audio """ + function: str = "noise-removal" input_type: str = DataType.AUDIO output_type: str = DataType.AUDIO @@ -1899,13 +1945,14 @@ def __init__(self, node=None): class AudioReconstruction(BaseReconstructor[AudioReconstructionInputs, AudioReconstructionOutputs]): """ - Audio Reconstruction is the process of restoring or recreating audio signals -from incomplete, damaged, or degraded recordings to achieve a high-quality, -accurate representation of the original sound. + Audio Reconstruction is the process of restoring or recreating audio signals + from incomplete, damaged, or degraded recordings to achieve a high-quality, + accurate representation of the original sound. - InputType: audio - OutputType: audio + InputType: audio + OutputType: audio """ + function: str = "audio-reconstruction" input_type: str = DataType.AUDIO output_type: str = DataType.AUDIO @@ -1944,13 +1991,14 @@ def __init__(self, node=None): class VoiceCloning(AssetNode[VoiceCloningInputs, VoiceCloningOutputs]): """ - Voice cloning is a technology that uses artificial intelligence to create a -digital replica of a person's voice, allowing for the generation of speech that -mimics the tone, pitch, and speaking style of the original speaker. + Voice cloning is a technology that uses artificial intelligence to create a + digital replica of a person's voice, allowing for the generation of speech that + mimics the tone, pitch, and speaking style of the original speaker. - InputType: text - OutputType: audio + InputType: text + OutputType: audio """ + function: str = "voice-cloning" input_type: str = DataType.TEXT output_type: str = DataType.AUDIO @@ -1983,14 +2031,15 @@ def __init__(self, node=None): class Diacritization(AssetNode[DiacritizationInputs, DiacritizationOutputs]): """ - Diacritization is the process of adding diacritical marks to letters in a text -to indicate pronunciation, stress, tone, or meaning, often used in languages -such as Arabic, Hebrew, and Vietnamese to provide clarity and accuracy in -written communication. + Diacritization is the process of adding diacritical marks to letters in a text + to indicate pronunciation, stress, tone, or meaning, often used in languages + such as Arabic, Hebrew, and Vietnamese to provide clarity and accuracy in + written communication. - InputType: text - OutputType: text + InputType: text + OutputType: text """ + function: str = "diacritization" input_type: str = DataType.TEXT output_type: str = DataType.TEXT @@ -2017,13 +2066,14 @@ def __init__(self, node=None): class AudioEmotionDetection(AssetNode[AudioEmotionDetectionInputs, AudioEmotionDetectionOutputs]): """ - Audio Emotion Detection is a technology that analyzes vocal characteristics and -patterns in audio recordings to identify and classify the emotional state of -the speaker. + Audio Emotion Detection is a technology that analyzes vocal characteristics and + patterns in audio recordings to identify and classify the emotional state of + the speaker. - InputType: audio - OutputType: label + InputType: audio + OutputType: label """ + function: str = "audio-emotion-detection" input_type: str = DataType.AUDIO output_type: str = DataType.LABEL @@ -2056,13 +2106,14 @@ def __init__(self, node=None): class TextSummarization(AssetNode[TextSummarizationInputs, TextSummarizationOutputs]): """ - Text summarization is the process of condensing a large body of text into a -shorter version, capturing the main points and essential information while -maintaining coherence and meaning. + Text summarization is the process of condensing a large body of text into a + shorter version, capturing the main points and essential information while + maintaining coherence and meaning. - InputType: text - OutputType: text + InputType: text + OutputType: text """ + function: str = "text-summarization" input_type: str = DataType.TEXT output_type: str = DataType.TEXT @@ -2093,14 +2144,15 @@ def __init__(self, node=None): class EntityLinking(AssetNode[EntityLinkingInputs, EntityLinkingOutputs]): """ - Entity Linking is the process of identifying and connecting mentions of -entities within a text to their corresponding entries in a structured knowledge -base, thereby enabling the disambiguation of terms and enhancing the -understanding of the text's context. + Entity Linking is the process of identifying and connecting mentions of + entities within a text to their corresponding entries in a structured knowledge + base, thereby enabling the disambiguation of terms and enhancing the + understanding of the text's context. - InputType: text - OutputType: label + InputType: text + OutputType: label """ + function: str = "entity-linking" input_type: str = DataType.TEXT output_type: str = DataType.LABEL @@ -2133,14 +2185,15 @@ def __init__(self, node=None): class TextGenerationMetric(BaseMetric[TextGenerationMetricInputs, TextGenerationMetricOutputs]): """ - A Text Generation Metric is a quantitative measure used to evaluate the quality -and effectiveness of text produced by natural language processing models, often -assessing aspects such as coherence, relevance, fluency, and adherence to given -prompts or instructions. + A Text Generation Metric is a quantitative measure used to evaluate the quality + and effectiveness of text produced by natural language processing models, often + assessing aspects such as coherence, relevance, fluency, and adherence to given + prompts or instructions. - InputType: text - OutputType: text + InputType: text + OutputType: text """ + function: str = "text-generation-metric" input_type: str = DataType.TEXT output_type: str = DataType.TEXT @@ -2169,12 +2222,13 @@ def __init__(self, node=None): class SplitOnLinebreak(BaseSegmentor[SplitOnLinebreakInputs, SplitOnLinebreakOutputs]): """ - The "Split On Linebreak" function divides a given string into a list of -substrings, using linebreaks (newline characters) as the points of separation. + The "Split On Linebreak" function divides a given string into a list of + substrings, using linebreaks (newline characters) as the points of separation. - InputType: text - OutputType: text + InputType: text + OutputType: text """ + function: str = "split-on-linebreak" input_type: str = DataType.TEXT output_type: str = DataType.TEXT @@ -2207,14 +2261,15 @@ def __init__(self, node=None): class SentimentAnalysis(AssetNode[SentimentAnalysisInputs, SentimentAnalysisOutputs]): """ - Sentiment Analysis is a natural language processing technique used to determine -and classify the emotional tone or subjective information expressed in a piece -of text, such as identifying whether the sentiment is positive, negative, or -neutral. + Sentiment Analysis is a natural language processing technique used to determine + and classify the emotional tone or subjective information expressed in a piece + of text, such as identifying whether the sentiment is positive, negative, or + neutral. - InputType: text - OutputType: label + InputType: text + OutputType: label """ + function: str = "sentiment-analysis" input_type: str = DataType.TEXT output_type: str = DataType.LABEL @@ -2241,13 +2296,14 @@ def __init__(self, node=None): class KeywordSpotting(AssetNode[KeywordSpottingInputs, KeywordSpottingOutputs]): """ - Keyword Spotting is a function that enables the detection and identification of -specific words or phrases within a stream of audio, often used in voice- -activated systems to trigger actions or commands based on recognized keywords. + Keyword Spotting is a function that enables the detection and identification of + specific words or phrases within a stream of audio, often used in voice- + activated systems to trigger actions or commands based on recognized keywords. - InputType: audio - OutputType: label + InputType: audio + OutputType: label """ + function: str = "keyword-spotting" input_type: str = DataType.AUDIO output_type: str = DataType.LABEL @@ -2280,14 +2336,15 @@ def __init__(self, node=None): class TextClassification(AssetNode[TextClassificationInputs, TextClassificationOutputs]): """ - Text Classification is a natural language processing task that involves -categorizing text into predefined labels or classes based on its content, -enabling automated organization, filtering, and analysis of large volumes of -textual data. + Text Classification is a natural language processing task that involves + categorizing text into predefined labels or classes based on its content, + enabling automated organization, filtering, and analysis of large volumes of + textual data. - InputType: text - OutputType: label + InputType: text + OutputType: label """ + function: str = "text-classification" input_type: str = DataType.TEXT output_type: str = DataType.LABEL @@ -2316,14 +2373,15 @@ def __init__(self, node=None): class OtherMultipurpose(AssetNode[OtherMultipurposeInputs, OtherMultipurposeOutputs]): """ - The "Other (Multipurpose)" function serves as a versatile category designed to -accommodate a wide range of tasks and activities that do not fit neatly into -predefined classifications, offering flexibility and adaptability for various -needs. + The "Other (Multipurpose)" function serves as a versatile category designed to + accommodate a wide range of tasks and activities that do not fit neatly into + predefined classifications, offering flexibility and adaptability for various + needs. - InputType: text - OutputType: text + InputType: text + OutputType: text """ + function: str = "other-(multipurpose)" input_type: str = DataType.TEXT output_type: str = DataType.TEXT @@ -2362,13 +2420,14 @@ def __init__(self, node=None): class SpeechSynthesis(AssetNode[SpeechSynthesisInputs, SpeechSynthesisOutputs]): """ - Speech synthesis is the artificial production of human speech, typically -achieved through software or hardware systems that convert text into spoken -words, enabling machines to communicate verbally with users. + Speech synthesis is the artificial production of human speech, typically + achieved through software or hardware systems that convert text into spoken + words, enabling machines to communicate verbally with users. - InputType: text - OutputType: audio + InputType: text + OutputType: audio """ + function: str = "speech-synthesis" input_type: str = DataType.TEXT output_type: str = DataType.AUDIO @@ -2395,14 +2454,15 @@ def __init__(self, node=None): class AudioIntentDetection(AssetNode[AudioIntentDetectionInputs, AudioIntentDetectionOutputs]): """ - Audio Intent Detection is a process that involves analyzing audio signals to -identify and interpret the underlying intentions or purposes behind spoken -words, enabling systems to understand and respond appropriately to human -speech. + Audio Intent Detection is a process that involves analyzing audio signals to + identify and interpret the underlying intentions or purposes behind spoken + words, enabling systems to understand and respond appropriately to human + speech. - InputType: audio - OutputType: label + InputType: audio + OutputType: label """ + function: str = "audio-intent-detection" input_type: str = DataType.AUDIO output_type: str = DataType.LABEL @@ -2431,14 +2491,15 @@ def __init__(self, node=None): class VideoLabelDetection(AssetNode[VideoLabelDetectionInputs, VideoLabelDetectionOutputs]): """ - Video Label Detection is a function that automatically identifies and tags -various objects, scenes, activities, and other relevant elements within a -video, providing descriptive labels that enhance searchability and content -organization. + Video Label Detection is a function that automatically identifies and tags + various objects, scenes, activities, and other relevant elements within a + video, providing descriptive labels that enhance searchability and content + organization. - InputType: video - OutputType: label + InputType: video + OutputType: label """ + function: str = "video-label-detection" input_type: str = DataType.VIDEO output_type: str = DataType.LABEL @@ -2467,13 +2528,14 @@ def __init__(self, node=None): class AsrQualityEstimation(AssetNode[AsrQualityEstimationInputs, AsrQualityEstimationOutputs]): """ - ASR Quality Estimation is a process that evaluates the accuracy and reliability -of automatic speech recognition systems by analyzing their performance in -transcribing spoken language into text. + ASR Quality Estimation is a process that evaluates the accuracy and reliability + of automatic speech recognition systems by analyzing their performance in + transcribing spoken language into text. - InputType: text - OutputType: label + InputType: text + OutputType: label """ + function: str = "asr-quality-estimation" input_type: str = DataType.TEXT output_type: str = DataType.LABEL @@ -2508,14 +2570,15 @@ def __init__(self, node=None): class AudioTranscriptAnalysis(AssetNode[AudioTranscriptAnalysisInputs, AudioTranscriptAnalysisOutputs]): """ - Audio Transcript Analysis is a process that involves converting spoken language -from audio recordings into written text, followed by examining and interpreting -the transcribed content to extract meaningful insights, identify patterns, and -derive actionable information. + Audio Transcript Analysis is a process that involves converting spoken language + from audio recordings into written text, followed by examining and interpreting + the transcribed content to extract meaningful insights, identify patterns, and + derive actionable information. - InputType: audio - OutputType: text + InputType: audio + OutputType: text """ + function: str = "audio-transcript-analysis" input_type: str = DataType.AUDIO output_type: str = DataType.TEXT @@ -2542,13 +2605,14 @@ def __init__(self, node=None): class Search(AssetNode[SearchInputs, SearchOutputs]): """ - The "Search" function allows users to input keywords or phrases to quickly -locate specific information, files, or content within a database, website, or -application. + The "Search" function allows users to input keywords or phrases to quickly + locate specific information, files, or content within a database, website, or + application. - InputType: text - OutputType: text + InputType: text + OutputType: text """ + function: str = "search" input_type: str = DataType.TEXT output_type: str = DataType.TEXT @@ -2585,13 +2649,14 @@ def __init__(self, node=None): class VideoForcedAlignment(AssetNode[VideoForcedAlignmentInputs, VideoForcedAlignmentOutputs]): """ - Video Forced Alignment is a process that synchronizes video footage with -corresponding audio tracks by precisely aligning the visual and auditory -elements, ensuring that the movements of speakers' lips match the spoken words. + Video Forced Alignment is a process that synchronizes video footage with + corresponding audio tracks by precisely aligning the visual and auditory + elements, ensuring that the movements of speakers' lips match the spoken words. - InputType: video - OutputType: video + InputType: video + OutputType: video """ + function: str = "video-forced-alignment" input_type: str = DataType.VIDEO output_type: str = DataType.VIDEO @@ -2624,13 +2689,14 @@ def __init__(self, node=None): class VisemeGeneration(AssetNode[VisemeGenerationInputs, VisemeGenerationOutputs]): """ - Viseme Generation is the process of creating visual representations of -phonemes, which are the distinct units of sound in speech, to synchronize lip -movements with spoken words in animations or virtual avatars. + Viseme Generation is the process of creating visual representations of + phonemes, which are the distinct units of sound in speech, to synchronize lip + movements with spoken words in animations or virtual avatars. - InputType: text - OutputType: label + InputType: text + OutputType: label """ + function: str = "viseme-generation" input_type: str = DataType.TEXT output_type: str = DataType.LABEL @@ -2663,13 +2729,14 @@ def __init__(self, node=None): class TopicClassification(AssetNode[TopicClassificationInputs, TopicClassificationOutputs]): """ - Topic Classification is a natural language processing function that categorizes -text into predefined topics or subjects based on its content, enabling -efficient organization and retrieval of information. + Topic Classification is a natural language processing function that categorizes + text into predefined topics or subjects based on its content, enabling + efficient organization and retrieval of information. - InputType: text - OutputType: label + InputType: text + OutputType: label """ + function: str = "topic-classification" input_type: str = DataType.TEXT output_type: str = DataType.LABEL @@ -2702,13 +2769,14 @@ def __init__(self, node=None): class OffensiveLanguageIdentification(AssetNode[OffensiveLanguageIdentificationInputs, OffensiveLanguageIdentificationOutputs]): """ - Offensive Language Identification is a function that analyzes text to detect -and flag language that is abusive, harmful, or inappropriate, helping to -maintain a respectful and safe communication environment. + Offensive Language Identification is a function that analyzes text to detect + and flag language that is abusive, harmful, or inappropriate, helping to + maintain a respectful and safe communication environment. - InputType: text - OutputType: label + InputType: text + OutputType: label """ + function: str = "offensive-language-identification" input_type: str = DataType.TEXT output_type: str = DataType.LABEL @@ -2745,13 +2813,14 @@ def __init__(self, node=None): class SpeechTranslation(AssetNode[SpeechTranslationInputs, SpeechTranslationOutputs]): """ - Speech Translation is a technology that converts spoken language in real-time -from one language to another, enabling seamless communication between speakers -of different languages. + Speech Translation is a technology that converts spoken language in real-time + from one language to another, enabling seamless communication between speakers + of different languages. - InputType: audio - OutputType: text + InputType: audio + OutputType: text """ + function: str = "speech-translation" input_type: str = DataType.AUDIO output_type: str = DataType.TEXT @@ -2786,14 +2855,15 @@ def __init__(self, node=None): class SpeakerDiarizationAudio(BaseSegmentor[SpeakerDiarizationAudioInputs, SpeakerDiarizationAudioOutputs]): """ - Speaker Diarization Audio is a process that involves segmenting an audio -recording into distinct sections, each corresponding to a different speaker, in -order to identify and differentiate between multiple speakers within the same -audio stream. + Speaker Diarization Audio is a process that involves segmenting an audio + recording into distinct sections, each corresponding to a different speaker, in + order to identify and differentiate between multiple speakers within the same + audio stream. - InputType: audio - OutputType: label + InputType: audio + OutputType: label """ + function: str = "speaker-diarization-audio" input_type: str = DataType.AUDIO output_type: str = DataType.LABEL @@ -2830,14 +2900,15 @@ def __init__(self, node=None): class AudioTranscriptImprovement(AssetNode[AudioTranscriptImprovementInputs, AudioTranscriptImprovementOutputs]): """ - Audio Transcript Improvement is a function that enhances the accuracy and -clarity of transcribed audio recordings by correcting errors, refining -language, and ensuring the text faithfully represents the original spoken -content. + Audio Transcript Improvement is a function that enhances the accuracy and + clarity of transcribed audio recordings by correcting errors, refining + language, and ensuring the text faithfully represents the original spoken + content. - InputType: audio - OutputType: text + InputType: audio + OutputType: text """ + function: str = "audio-transcript-improvement" input_type: str = DataType.AUDIO output_type: str = DataType.TEXT @@ -2870,14 +2941,15 @@ def __init__(self, node=None): class SpeechNonSpeechClassification(AssetNode[SpeechNonSpeechClassificationInputs, SpeechNonSpeechClassificationOutputs]): """ - The function "Speech or Non-Speech Classification" is designed to analyze audio -input and determine whether the sound is human speech or non-speech noise, -enabling applications such as voice recognition systems to filter out -irrelevant background sounds. + The function "Speech or Non-Speech Classification" is designed to analyze audio + input and determine whether the sound is human speech or non-speech noise, + enabling applications such as voice recognition systems to filter out + irrelevant background sounds. - InputType: audio - OutputType: label + InputType: audio + OutputType: label """ + function: str = "speech-non-speech-classification" input_type: str = DataType.AUDIO output_type: str = DataType.LABEL @@ -2912,13 +2984,14 @@ def __init__(self, node=None): class TextDenormalization(AssetNode[TextDenormalizationInputs, TextDenormalizationOutputs]): """ - Text Denormalization is the process of converting abbreviated, contracted, or -otherwise simplified text into its full, standard form, often to improve -readability and ensure consistency in natural language processing tasks. + Text Denormalization is the process of converting abbreviated, contracted, or + otherwise simplified text into its full, standard form, often to improve + readability and ensure consistency in natural language processing tasks. - InputType: text - OutputType: label + InputType: text + OutputType: label """ + function: str = "text-denormalization" input_type: str = DataType.TEXT output_type: str = DataType.LABEL @@ -2947,13 +3020,14 @@ def __init__(self, node=None): class ImageContentModeration(AssetNode[ImageContentModerationInputs, ImageContentModerationOutputs]): """ - Image Content Moderation is a process that involves analyzing and filtering -images to detect and manage inappropriate, harmful, or sensitive content, -ensuring compliance with community guidelines and legal standards. + Image Content Moderation is a process that involves analyzing and filtering + images to detect and manage inappropriate, harmful, or sensitive content, + ensuring compliance with community guidelines and legal standards. - InputType: image - OutputType: label + InputType: image + OutputType: label """ + function: str = "image-content-moderation" input_type: str = DataType.IMAGE output_type: str = DataType.LABEL @@ -2982,15 +3056,18 @@ def __init__(self, node=None): self.data = self.create_param(code="data", data_type=DataType.TEXT) -class ReferencelessTextGenerationMetricDefault(BaseMetric[ReferencelessTextGenerationMetricDefaultInputs, ReferencelessTextGenerationMetricDefaultOutputs]): +class ReferencelessTextGenerationMetricDefault( + BaseMetric[ReferencelessTextGenerationMetricDefaultInputs, ReferencelessTextGenerationMetricDefaultOutputs] +): """ - The Referenceless Text Generation Metric Default is a function designed to -evaluate the quality of generated text without relying on reference texts for -comparison. + The Referenceless Text Generation Metric Default is a function designed to + evaluate the quality of generated text without relying on reference texts for + comparison. - InputType: text - OutputType: text + InputType: text + OutputType: text """ + function: str = "referenceless-text-generation-metric-default" input_type: str = DataType.TEXT output_type: str = DataType.TEXT @@ -3025,14 +3102,15 @@ def __init__(self, node=None): class NamedEntityRecognition(AssetNode[NamedEntityRecognitionInputs, NamedEntityRecognitionOutputs]): """ - Named Entity Recognition (NER) is a natural language processing task that -involves identifying and classifying proper nouns in text into predefined -categories such as names of people, organizations, locations, dates, and other -entities. + Named Entity Recognition (NER) is a natural language processing task that + involves identifying and classifying proper nouns in text into predefined + categories such as names of people, organizations, locations, dates, and other + entities. - InputType: text - OutputType: label + InputType: text + OutputType: label """ + function: str = "named-entity-recognition" input_type: str = DataType.TEXT output_type: str = DataType.LABEL @@ -3065,14 +3143,15 @@ def __init__(self, node=None): class TextContentModeration(AssetNode[TextContentModerationInputs, TextContentModerationOutputs]): """ - Text Content Moderation is the process of reviewing, filtering, and managing -user-generated content to ensure it adheres to community guidelines, legal -standards, and platform policies, thereby maintaining a safe and respectful -online environment. + Text Content Moderation is the process of reviewing, filtering, and managing + user-generated content to ensure it adheres to community guidelines, legal + standards, and platform policies, thereby maintaining a safe and respectful + online environment. - InputType: text - OutputType: label + InputType: text + OutputType: label """ + function: str = "text-content-moderation" input_type: str = DataType.TEXT output_type: str = DataType.LABEL @@ -3105,13 +3184,14 @@ def __init__(self, node=None): class SpeakerDiarizationVideo(AssetNode[SpeakerDiarizationVideoInputs, SpeakerDiarizationVideoOutputs]): """ - The Speaker Diarization Video function identifies and segments different -speakers in a video, attributing portions of the audio to individual speakers -to facilitate analysis and understanding of multi-speaker conversations. + The Speaker Diarization Video function identifies and segments different + speakers in a video, attributing portions of the audio to individual speakers + to facilitate analysis and understanding of multi-speaker conversations. - InputType: video - OutputType: label + InputType: video + OutputType: label """ + function: str = "speaker-diarization-video" input_type: str = DataType.VIDEO output_type: str = DataType.LABEL @@ -3138,13 +3218,14 @@ def __init__(self, node=None): class SplitOnSilence(AssetNode[SplitOnSilenceInputs, SplitOnSilenceOutputs]): """ - The "Split On Silence" function divides an audio recording into separate -segments based on periods of silence, allowing for easier editing and analysis -of individual sections. + The "Split On Silence" function divides an audio recording into separate + segments based on periods of silence, allowing for easier editing and analysis + of individual sections. - InputType: audio - OutputType: audio + InputType: audio + OutputType: audio """ + function: str = "split-on-silence" input_type: str = DataType.AUDIO output_type: str = DataType.AUDIO @@ -3177,13 +3258,14 @@ def __init__(self, node=None): class EmotionDetection(AssetNode[EmotionDetectionInputs, EmotionDetectionOutputs]): """ - Emotion Detection is a process that involves analyzing text to identify and -categorize the emotional states or sentiments expressed by individuals, such as -happiness, sadness, anger, or fear. + Emotion Detection is a process that involves analyzing text to identify and + categorize the emotional states or sentiments expressed by individuals, such as + happiness, sadness, anger, or fear. - InputType: text - OutputType: label + InputType: text + OutputType: label """ + function: str = "emotion-detection" input_type: str = DataType.TEXT output_type: str = DataType.LABEL @@ -3216,14 +3298,15 @@ def __init__(self, node=None): class TextSpamDetection(AssetNode[TextSpamDetectionInputs, TextSpamDetectionOutputs]): """ - Text Spam Detection is a process that involves analyzing and identifying -unsolicited or irrelevant messages within text communications, typically using -algorithms and machine learning techniques to filter out spam and ensure the -integrity of the communication platform. + Text Spam Detection is a process that involves analyzing and identifying + unsolicited or irrelevant messages within text communications, typically using + algorithms and machine learning techniques to filter out spam and ensure the + integrity of the communication platform. - InputType: text - OutputType: label + InputType: text + OutputType: label """ + function: str = "text-spam-detection" input_type: str = DataType.TEXT output_type: str = DataType.LABEL @@ -3264,13 +3347,14 @@ def __init__(self, node=None): class Translation(AssetNode[TranslationInputs, TranslationOutputs]): """ - Translation is the process of converting text from one language into an -equivalent text in another language, preserving the original meaning and -context. + Translation is the process of converting text from one language into an + equivalent text in another language, preserving the original meaning and + context. - InputType: text - OutputType: text + InputType: text + OutputType: text """ + function: str = "translation" input_type: str = DataType.TEXT output_type: str = DataType.TEXT @@ -3307,13 +3391,14 @@ def __init__(self, node=None): class VoiceActivityDetection(BaseSegmentor[VoiceActivityDetectionInputs, VoiceActivityDetectionOutputs]): """ - Voice Activity Detection (VAD) is a technology that identifies the presence or -absence of human speech within an audio signal, enabling systems to distinguish -between spoken words and background noise. + Voice Activity Detection (VAD) is a technology that identifies the presence or + absence of human speech within an audio signal, enabling systems to distinguish + between spoken words and background noise. - InputType: audio - OutputType: audio + InputType: audio + OutputType: audio """ + function: str = "voice-activity-detection" input_type: str = DataType.AUDIO output_type: str = DataType.AUDIO @@ -3346,14 +3431,15 @@ def __init__(self, node=None): class SpeechEmbedding(AssetNode[SpeechEmbeddingInputs, SpeechEmbeddingOutputs]): """ - Speech Embedding is a process that transforms spoken language into a fixed- -dimensional vector representation, capturing essential features and -characteristics of the speech for tasks such as recognition, classification, -and analysis. + Speech Embedding is a process that transforms spoken language into a fixed- + dimensional vector representation, capturing essential features and + characteristics of the speech for tasks such as recognition, classification, + and analysis. - InputType: audio - OutputType: text + InputType: audio + OutputType: text """ + function: str = "speech-embedding" input_type: str = DataType.AUDIO output_type: str = DataType.TEXT @@ -3388,13 +3474,14 @@ def __init__(self, node=None): class SubtitlingTranslation(AssetNode[SubtitlingTranslationInputs, SubtitlingTranslationOutputs]): """ - Subtitling Translation is the process of converting spoken dialogue from one -language into written text in another language, which is then displayed on- -screen to aid viewers in understanding the content. + Subtitling Translation is the process of converting spoken dialogue from one + language into written text in another language, which is then displayed on- + screen to aid viewers in understanding the content. - InputType: text - OutputType: text + InputType: text + OutputType: text """ + function: str = "subtitling-translation" input_type: str = DataType.TEXT output_type: str = DataType.TEXT @@ -3429,13 +3516,14 @@ def __init__(self, node=None): class TextGeneration(AssetNode[TextGenerationInputs, TextGenerationOutputs]): """ - Text Generation is a process in which artificial intelligence models, such as -neural networks, produce coherent and contextually relevant text based on a -given input or prompt, often mimicking human writing styles and patterns. + Text Generation is a process in which artificial intelligence models, such as + neural networks, produce coherent and contextually relevant text based on a + given input or prompt, often mimicking human writing styles and patterns. - InputType: text - OutputType: text + InputType: text + OutputType: text """ + function: str = "text-generation" input_type: str = DataType.TEXT output_type: str = DataType.TEXT @@ -3470,13 +3558,14 @@ def __init__(self, node=None): class VideoUnderstanding(AssetNode[VideoUnderstandingInputs, VideoUnderstandingOutputs]): """ - Video Understanding is the process of analyzing and interpreting video content -to extract meaningful information, such as identifying objects, actions, -events, and contextual relationships within the footage. + Video Understanding is the process of analyzing and interpreting video content + to extract meaningful information, such as identifying objects, actions, + events, and contextual relationships within the footage. - InputType: video - OutputType: text + InputType: video + OutputType: text """ + function: str = "video-understanding" input_type: str = DataType.VIDEO output_type: str = DataType.TEXT @@ -3505,13 +3594,14 @@ def __init__(self, node=None): class TextToVideoGeneration(AssetNode[TextToVideoGenerationInputs, TextToVideoGenerationOutputs]): """ - Text To Video Generation is a process that converts written descriptions or -scripts into dynamic, visual video content using advanced algorithms and -artificial intelligence. + Text To Video Generation is a process that converts written descriptions or + scripts into dynamic, visual video content using advanced algorithms and + artificial intelligence. - InputType: text - OutputType: video + InputType: text + OutputType: video """ + function: str = "text-to-video-generation" input_type: str = DataType.TEXT output_type: str = DataType.VIDEO @@ -3542,14 +3632,15 @@ def __init__(self, node=None): class TextNormalization(AssetNode[TextNormalizationInputs, TextNormalizationOutputs]): """ - Text normalization is the process of transforming text into a standard, -consistent format by correcting spelling errors, converting all characters to a -uniform case, removing punctuation, and expanding abbreviations to improve the -text's readability and usability for further processing or analysis. + Text normalization is the process of transforming text into a standard, + consistent format by correcting spelling errors, converting all characters to a + uniform case, removing punctuation, and expanding abbreviations to improve the + text's readability and usability for further processing or analysis. - InputType: text - OutputType: label + InputType: text + OutputType: label """ + function: str = "text-normalization" input_type: str = DataType.TEXT output_type: str = DataType.LABEL @@ -3584,12 +3675,13 @@ def __init__(self, node=None): class SpeechRecognition(AssetNode[SpeechRecognitionInputs, SpeechRecognitionOutputs]): """ - Speech recognition is a technology that enables a computer or device to -identify and process spoken language, converting it into text. + Speech recognition is a technology that enables a computer or device to + identify and process spoken language, converting it into text. - InputType: audio - OutputType: text + InputType: audio + OutputType: text """ + function: str = "speech-recognition" input_type: str = DataType.AUDIO output_type: str = DataType.TEXT @@ -3626,14 +3718,15 @@ def __init__(self, node=None): class Subtitling(AssetNode[SubtitlingInputs, SubtitlingOutputs]): """ - Subtitling is the process of displaying written text on a screen to represent -the spoken dialogue, narration, or other audio elements in a video, typically -to aid viewers who are deaf or hard of hearing, or to provide translations for -audiences who speak different languages. + Subtitling is the process of displaying written text on a screen to represent + the spoken dialogue, narration, or other audio elements in a video, typically + to aid viewers who are deaf or hard of hearing, or to provide translations for + audiences who speak different languages. - InputType: audio - OutputType: text + InputType: audio + OutputType: text """ + function: str = "subtitling" input_type: str = DataType.AUDIO output_type: str = DataType.TEXT @@ -3668,12 +3761,13 @@ def __init__(self, node=None): class ClassificationMetric(BaseMetric[ClassificationMetricInputs, ClassificationMetricOutputs]): """ - A Classification Metric is a quantitative measure used to evaluate the quality -and effectiveness of classification models. + A Classification Metric is a quantitative measure used to evaluate the quality + and effectiveness of classification models. - InputType: text - OutputType: text + InputType: text + OutputType: text """ + function: str = "classification-metric" input_type: str = DataType.TEXT output_type: str = DataType.TEXT @@ -3700,13 +3794,14 @@ def __init__(self, node=None): class TextToImageGeneration(AssetNode[TextToImageGenerationInputs, TextToImageGenerationOutputs]): """ - Text To Image Generation is a process where a system creates visual images -based on descriptive text input, translating written language into -corresponding graphical representations. + Text To Image Generation is a process where a system creates visual images + based on descriptive text input, translating written language into + corresponding graphical representations. - InputType: text - OutputType: image + InputType: text + OutputType: image """ + function: str = "text-to-image-generation" input_type: str = DataType.TEXT output_type: str = DataType.IMAGE @@ -3715,835 +3810,852 @@ class TextToImageGeneration(AssetNode[TextToImageGenerationInputs, TextToImageGe outputs_class: Type[TO] = TextToImageGenerationOutputs - class Pipeline(DefaultPipeline): - def object_detection(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> ObjectDetection: """ - Object Detection is a computer vision technology that identifies and locates -objects within an image, typically by drawing bounding boxes around the -detected objects and classifying them into predefined categories. + Object Detection is a computer vision technology that identifies and locates + objects within an image, typically by drawing bounding boxes around the + detected objects and classifying them into predefined categories. """ return ObjectDetection(*args, asset_id=asset_id, pipeline=self, **kwargs) def language_identification(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> LanguageIdentification: """ - Language Identification is the process of automatically determining the -language in which a given piece of text is written. + Language Identification is the process of automatically determining the + language in which a given piece of text is written. """ return LanguageIdentification(*args, asset_id=asset_id, pipeline=self, **kwargs) def ocr(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> Ocr: """ - OCR, or Optical Character Recognition, is a technology that converts different -types of documents, such as scanned paper documents, PDFs, or images captured -by a digital camera, into editable and searchable data by recognizing and -extracting text from the images. + OCR, or Optical Character Recognition, is a technology that converts different + types of documents, such as scanned paper documents, PDFs, or images captured + by a digital camera, into editable and searchable data by recognizing and + extracting text from the images. """ return Ocr(*args, asset_id=asset_id, pipeline=self, **kwargs) def script_execution(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> ScriptExecution: """ - Script Execution refers to the process of running a set of programmed -instructions or code within a computing environment, enabling the automated -performance of tasks, calculations, or operations as defined by the script. + Script Execution refers to the process of running a set of programmed + instructions or code within a computing environment, enabling the automated + performance of tasks, calculations, or operations as defined by the script. """ return ScriptExecution(*args, asset_id=asset_id, pipeline=self, **kwargs) def image_label_detection(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> ImageLabelDetection: """ - Image Label Detection is a function that automatically identifies and assigns -descriptive tags or labels to objects, scenes, or elements within an image, -enabling easier categorization, search, and analysis of visual content. + Image Label Detection is a function that automatically identifies and assigns + descriptive tags or labels to objects, scenes, or elements within an image, + enabling easier categorization, search, and analysis of visual content. """ return ImageLabelDetection(*args, asset_id=asset_id, pipeline=self, **kwargs) def image_captioning(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> ImageCaptioning: """ - Image Captioning is a process that involves generating a textual description of -an image, typically using machine learning models to analyze the visual content -and produce coherent and contextually relevant sentences that describe the -objects, actions, and scenes depicted in the image. + Image Captioning is a process that involves generating a textual description of + an image, typically using machine learning models to analyze the visual content + and produce coherent and contextually relevant sentences that describe the + objects, actions, and scenes depicted in the image. """ return ImageCaptioning(*args, asset_id=asset_id, pipeline=self, **kwargs) def audio_language_identification(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> AudioLanguageIdentification: """ - Audio Language Identification is a process that involves analyzing an audio -recording to determine the language being spoken. + Audio Language Identification is a process that involves analyzing an audio + recording to determine the language being spoken. """ return AudioLanguageIdentification(*args, asset_id=asset_id, pipeline=self, **kwargs) def asr_age_classification(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> AsrAgeClassification: """ - The ASR Age Classification function is designed to analyze audio recordings of -speech to determine the speaker's age group by leveraging automatic speech -recognition (ASR) technology and machine learning algorithms. + The ASR Age Classification function is designed to analyze audio recordings of + speech to determine the speaker's age group by leveraging automatic speech + recognition (ASR) technology and machine learning algorithms. """ return AsrAgeClassification(*args, asset_id=asset_id, pipeline=self, **kwargs) def benchmark_scoring_mt(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> BenchmarkScoringMt: """ - Benchmark Scoring MT is a function designed to evaluate and score machine -translation systems by comparing their output against a set of predefined -benchmarks, thereby assessing their accuracy and performance. + Benchmark Scoring MT is a function designed to evaluate and score machine + translation systems by comparing their output against a set of predefined + benchmarks, thereby assessing their accuracy and performance. """ return BenchmarkScoringMt(*args, asset_id=asset_id, pipeline=self, **kwargs) def asr_gender_classification(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> AsrGenderClassification: """ - The ASR Gender Classification function analyzes audio recordings to determine -and classify the speaker's gender based on their voice characteristics. + The ASR Gender Classification function analyzes audio recordings to determine + and classify the speaker's gender based on their voice characteristics. """ return AsrGenderClassification(*args, asset_id=asset_id, pipeline=self, **kwargs) def base_model(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> BaseModel: """ - The Base-Model function serves as a foundational framework designed to provide -essential features and capabilities upon which more specialized or advanced -models can be built and customized. + The Base-Model function serves as a foundational framework designed to provide + essential features and capabilities upon which more specialized or advanced + models can be built and customized. """ return BaseModel(*args, asset_id=asset_id, pipeline=self, **kwargs) def language_identification_audio(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> LanguageIdentificationAudio: """ - The Language Identification Audio function analyzes audio input to determine -and identify the language being spoken. + The Language Identification Audio function analyzes audio input to determine + and identify the language being spoken. """ return LanguageIdentificationAudio(*args, asset_id=asset_id, pipeline=self, **kwargs) def loglikelihood(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> Loglikelihood: """ - The Log Likelihood function measures the probability of observing the given -data under a specific statistical model by taking the natural logarithm of the -likelihood function, thereby transforming the product of probabilities into a -sum, which simplifies the process of optimization and parameter estimation. + The Log Likelihood function measures the probability of observing the given + data under a specific statistical model by taking the natural logarithm of the + likelihood function, thereby transforming the product of probabilities into a + sum, which simplifies the process of optimization and parameter estimation. """ return Loglikelihood(*args, asset_id=asset_id, pipeline=self, **kwargs) def video_embedding(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> VideoEmbedding: """ - Video Embedding is a process that transforms video content into a fixed- -dimensional vector representation, capturing essential features and patterns to -facilitate tasks such as retrieval, classification, and recommendation. + Video Embedding is a process that transforms video content into a fixed- + dimensional vector representation, capturing essential features and patterns to + facilitate tasks such as retrieval, classification, and recommendation. """ return VideoEmbedding(*args, asset_id=asset_id, pipeline=self, **kwargs) def text_segmenation(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> TextSegmenation: """ - Text Segmentation is the process of dividing a continuous text into meaningful -units, such as words, sentences, or topics, to facilitate easier analysis and -understanding. + Text Segmentation is the process of dividing a continuous text into meaningful + units, such as words, sentences, or topics, to facilitate easier analysis and + understanding. """ return TextSegmenation(*args, asset_id=asset_id, pipeline=self, **kwargs) def image_embedding(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> ImageEmbedding: """ - Image Embedding is a process that transforms an image into a fixed-dimensional -vector representation, capturing its essential features and enabling efficient -comparison, retrieval, and analysis in various machine learning and computer -vision tasks. + Image Embedding is a process that transforms an image into a fixed-dimensional + vector representation, capturing its essential features and enabling efficient + comparison, retrieval, and analysis in various machine learning and computer + vision tasks. """ return ImageEmbedding(*args, asset_id=asset_id, pipeline=self, **kwargs) def image_manipulation(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> ImageManipulation: """ - Image Manipulation refers to the process of altering or enhancing digital -images using various techniques and tools to achieve desired visual effects, -correct imperfections, or transform the image's appearance. + Image Manipulation refers to the process of altering or enhancing digital + images using various techniques and tools to achieve desired visual effects, + correct imperfections, or transform the image's appearance. """ return ImageManipulation(*args, asset_id=asset_id, pipeline=self, **kwargs) def image_to_video_generation(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> ImageToVideoGeneration: """ - The Image To Video Generation function transforms a series of static images -into a cohesive, dynamic video sequence, often incorporating transitions, -effects, and synchronization with audio to create a visually engaging -narrative. + The Image To Video Generation function transforms a series of static images + into a cohesive, dynamic video sequence, often incorporating transitions, + effects, and synchronization with audio to create a visually engaging + narrative. """ return ImageToVideoGeneration(*args, asset_id=asset_id, pipeline=self, **kwargs) def audio_forced_alignment(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> AudioForcedAlignment: """ - Audio Forced Alignment is a process that synchronizes a given audio recording -with its corresponding transcript by precisely aligning each spoken word or -phoneme to its exact timing within the audio. + Audio Forced Alignment is a process that synchronizes a given audio recording + with its corresponding transcript by precisely aligning each spoken word or + phoneme to its exact timing within the audio. """ return AudioForcedAlignment(*args, asset_id=asset_id, pipeline=self, **kwargs) def benchmark_scoring_asr(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> BenchmarkScoringAsr: """ - Benchmark Scoring ASR is a function that evaluates and compares the performance -of automatic speech recognition systems by analyzing their accuracy, speed, and -other relevant metrics against a standardized set of benchmarks. + Benchmark Scoring ASR is a function that evaluates and compares the performance + of automatic speech recognition systems by analyzing their accuracy, speed, and + other relevant metrics against a standardized set of benchmarks. """ return BenchmarkScoringAsr(*args, asset_id=asset_id, pipeline=self, **kwargs) def visual_question_answering(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> VisualQuestionAnswering: """ - Visual Question Answering (VQA) is a task in artificial intelligence that -involves analyzing an image and providing accurate, contextually relevant -answers to questions posed about the visual content of that image. + Visual Question Answering (VQA) is a task in artificial intelligence that + involves analyzing an image and providing accurate, contextually relevant + answers to questions posed about the visual content of that image. """ return VisualQuestionAnswering(*args, asset_id=asset_id, pipeline=self, **kwargs) def document_image_parsing(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> DocumentImageParsing: """ - Document Image Parsing is the process of analyzing and converting scanned or -photographed images of documents into structured, machine-readable formats by -identifying and extracting text, layout, and other relevant information. + Document Image Parsing is the process of analyzing and converting scanned or + photographed images of documents into structured, machine-readable formats by + identifying and extracting text, layout, and other relevant information. """ return DocumentImageParsing(*args, asset_id=asset_id, pipeline=self, **kwargs) - def document_information_extraction(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> DocumentInformationExtraction: + def document_information_extraction( + self, asset_id: Union[str, asset.Asset], *args, **kwargs + ) -> DocumentInformationExtraction: """ - Document Information Extraction is the process of automatically identifying, -extracting, and structuring relevant data from unstructured or semi-structured -documents, such as invoices, receipts, contracts, and forms, to facilitate -easier data management and analysis. + Document Information Extraction is the process of automatically identifying, + extracting, and structuring relevant data from unstructured or semi-structured + documents, such as invoices, receipts, contracts, and forms, to facilitate + easier data management and analysis. """ return DocumentInformationExtraction(*args, asset_id=asset_id, pipeline=self, **kwargs) def depth_estimation(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> DepthEstimation: """ - Depth estimation is a computational process that determines the distance of -objects from a viewpoint, typically using visual data from cameras or sensors -to create a three-dimensional understanding of a scene. + Depth estimation is a computational process that determines the distance of + objects from a viewpoint, typically using visual data from cameras or sensors + to create a three-dimensional understanding of a scene. """ return DepthEstimation(*args, asset_id=asset_id, pipeline=self, **kwargs) def video_generation(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> VideoGeneration: """ - Video Generation is the process of creating video content through automated or -semi-automated means, often utilizing algorithms, artificial intelligence, or -software tools to produce visual and audio elements that can range from simple -animations to complex, realistic scenes. + Video Generation is the process of creating video content through automated or + semi-automated means, often utilizing algorithms, artificial intelligence, or + software tools to produce visual and audio elements that can range from simple + animations to complex, realistic scenes. """ return VideoGeneration(*args, asset_id=asset_id, pipeline=self, **kwargs) - def referenceless_audio_generation_metric(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> ReferencelessAudioGenerationMetric: + def referenceless_audio_generation_metric( + self, asset_id: Union[str, asset.Asset], *args, **kwargs + ) -> ReferencelessAudioGenerationMetric: """ - The Referenceless Audio Generation Metric is a tool designed to evaluate the -quality of generated audio content without the need for a reference or original -audio sample for comparison. + The Referenceless Audio Generation Metric is a tool designed to evaluate the + quality of generated audio content without the need for a reference or original + audio sample for comparison. """ return ReferencelessAudioGenerationMetric(*args, asset_id=asset_id, pipeline=self, **kwargs) - def multi_class_image_classification(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> MultiClassImageClassification: + def multi_class_image_classification( + self, asset_id: Union[str, asset.Asset], *args, **kwargs + ) -> MultiClassImageClassification: """ - Multi Class Image Classification is a machine learning task where an algorithm -is trained to categorize images into one of several predefined classes or -categories based on their visual content. + Multi Class Image Classification is a machine learning task where an algorithm + is trained to categorize images into one of several predefined classes or + categories based on their visual content. """ return MultiClassImageClassification(*args, asset_id=asset_id, pipeline=self, **kwargs) def semantic_segmentation(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> SemanticSegmentation: """ - Semantic segmentation is a computer vision process that involves classifying -each pixel in an image into a predefined category, effectively partitioning the -image into meaningful segments based on the objects or regions they represent. + Semantic segmentation is a computer vision process that involves classifying + each pixel in an image into a predefined category, effectively partitioning the + image into meaningful segments based on the objects or regions they represent. """ return SemanticSegmentation(*args, asset_id=asset_id, pipeline=self, **kwargs) def instance_segmentation(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> InstanceSegmentation: """ - Instance segmentation is a computer vision task that involves detecting and -delineating each distinct object within an image, assigning a unique label and -precise boundary to every individual instance of objects, even if they belong -to the same category. + Instance segmentation is a computer vision task that involves detecting and + delineating each distinct object within an image, assigning a unique label and + precise boundary to every individual instance of objects, even if they belong + to the same category. """ return InstanceSegmentation(*args, asset_id=asset_id, pipeline=self, **kwargs) def image_colorization(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> ImageColorization: """ - Image colorization is a process that involves adding color to grayscale images, -transforming them from black-and-white to full-color representations, often -using advanced algorithms and machine learning techniques to predict and apply -the appropriate hues and shades. + Image colorization is a process that involves adding color to grayscale images, + transforming them from black-and-white to full-color representations, often + using advanced algorithms and machine learning techniques to predict and apply + the appropriate hues and shades. """ return ImageColorization(*args, asset_id=asset_id, pipeline=self, **kwargs) def audio_generation_metric(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> AudioGenerationMetric: """ - The Audio Generation Metric is a quantitative measure used to evaluate the -quality, accuracy, and overall performance of audio generated by artificial -intelligence systems, often considering factors such as fidelity, -intelligibility, and similarity to human-produced audio. + The Audio Generation Metric is a quantitative measure used to evaluate the + quality, accuracy, and overall performance of audio generated by artificial + intelligence systems, often considering factors such as fidelity, + intelligibility, and similarity to human-produced audio. """ return AudioGenerationMetric(*args, asset_id=asset_id, pipeline=self, **kwargs) def image_impainting(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> ImageImpainting: """ - Image inpainting is a process that involves filling in missing or damaged parts -of an image in a way that is visually coherent and seamlessly blends with the -surrounding areas, often using advanced algorithms and techniques to restore -the image to its original or intended appearance. + Image inpainting is a process that involves filling in missing or damaged parts + of an image in a way that is visually coherent and seamlessly blends with the + surrounding areas, often using advanced algorithms and techniques to restore + the image to its original or intended appearance. """ return ImageImpainting(*args, asset_id=asset_id, pipeline=self, **kwargs) def style_transfer(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> StyleTransfer: """ - Style Transfer is a technique in artificial intelligence that applies the -visual style of one image (such as the brushstrokes of a famous painting) to -the content of another image, effectively blending the artistic elements of the -first image with the subject matter of the second. + Style Transfer is a technique in artificial intelligence that applies the + visual style of one image (such as the brushstrokes of a famous painting) to + the content of another image, effectively blending the artistic elements of the + first image with the subject matter of the second. """ return StyleTransfer(*args, asset_id=asset_id, pipeline=self, **kwargs) - def multi_class_text_classification(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> MultiClassTextClassification: + def multi_class_text_classification( + self, asset_id: Union[str, asset.Asset], *args, **kwargs + ) -> MultiClassTextClassification: """ - Multi Class Text Classification is a natural language processing task that -involves categorizing a given text into one of several predefined classes or -categories based on its content. + Multi Class Text Classification is a natural language processing task that + involves categorizing a given text into one of several predefined classes or + categories based on its content. """ return MultiClassTextClassification(*args, asset_id=asset_id, pipeline=self, **kwargs) def text_embedding(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> TextEmbedding: """ - Text embedding is a process that converts text into numerical vectors, -capturing the semantic meaning and contextual relationships of words or -phrases, enabling machines to understand and analyze natural language more -effectively. + Text embedding is a process that converts text into numerical vectors, + capturing the semantic meaning and contextual relationships of words or + phrases, enabling machines to understand and analyze natural language more + effectively. """ return TextEmbedding(*args, asset_id=asset_id, pipeline=self, **kwargs) - def multi_label_text_classification(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> MultiLabelTextClassification: + def multi_label_text_classification( + self, asset_id: Union[str, asset.Asset], *args, **kwargs + ) -> MultiLabelTextClassification: """ - Multi Label Text Classification is a natural language processing task where a -given text is analyzed and assigned multiple relevant labels or categories from -a predefined set, allowing for the text to belong to more than one category -simultaneously. + Multi Label Text Classification is a natural language processing task where a + given text is analyzed and assigned multiple relevant labels or categories from + a predefined set, allowing for the text to belong to more than one category + simultaneously. """ return MultiLabelTextClassification(*args, asset_id=asset_id, pipeline=self, **kwargs) def text_reconstruction(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> TextReconstruction: """ - Text Reconstruction is a process that involves piecing together fragmented or -incomplete text data to restore it to its original, coherent form. + Text Reconstruction is a process that involves piecing together fragmented or + incomplete text data to restore it to its original, coherent form. """ return TextReconstruction(*args, asset_id=asset_id, pipeline=self, **kwargs) def fact_checking(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> FactChecking: """ - Fact Checking is the process of verifying the accuracy and truthfulness of -information, statements, or claims by cross-referencing with reliable sources -and evidence. + Fact Checking is the process of verifying the accuracy and truthfulness of + information, statements, or claims by cross-referencing with reliable sources + and evidence. """ return FactChecking(*args, asset_id=asset_id, pipeline=self, **kwargs) def speech_classification(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> SpeechClassification: """ - Speech Classification is a process that involves analyzing and categorizing -spoken language into predefined categories or classes based on various features -such as tone, pitch, and linguistic content. + Speech Classification is a process that involves analyzing and categorizing + spoken language into predefined categories or classes based on various features + such as tone, pitch, and linguistic content. """ return SpeechClassification(*args, asset_id=asset_id, pipeline=self, **kwargs) def intent_classification(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> IntentClassification: """ - Intent Classification is a natural language processing task that involves -analyzing and categorizing user text input to determine the underlying purpose -or goal behind the communication, such as booking a flight, asking for weather -information, or setting a reminder. + Intent Classification is a natural language processing task that involves + analyzing and categorizing user text input to determine the underlying purpose + or goal behind the communication, such as booking a flight, asking for weather + information, or setting a reminder. """ return IntentClassification(*args, asset_id=asset_id, pipeline=self, **kwargs) def part_of_speech_tagging(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> PartOfSpeechTagging: """ - Part of Speech Tagging is a natural language processing task that involves -assigning each word in a sentence its corresponding part of speech, such as -noun, verb, adjective, or adverb, based on its role and context within the -sentence. + Part of Speech Tagging is a natural language processing task that involves + assigning each word in a sentence its corresponding part of speech, such as + noun, verb, adjective, or adverb, based on its role and context within the + sentence. """ return PartOfSpeechTagging(*args, asset_id=asset_id, pipeline=self, **kwargs) def metric_aggregation(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> MetricAggregation: """ - Metric Aggregation is a function that computes and summarizes numerical data by -applying statistical operations, such as averaging, summing, or finding the -minimum and maximum values, to provide insights and facilitate analysis of -large datasets. + Metric Aggregation is a function that computes and summarizes numerical data by + applying statistical operations, such as averaging, summing, or finding the + minimum and maximum values, to provide insights and facilitate analysis of + large datasets. """ return MetricAggregation(*args, asset_id=asset_id, pipeline=self, **kwargs) def dialect_detection(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> DialectDetection: """ - Dialect Detection is a function that identifies and classifies the specific -regional or social variations of a language spoken or written by an individual, -enabling the recognition of distinct linguistic patterns and nuances associated -with different dialects. + Dialect Detection is a function that identifies and classifies the specific + regional or social variations of a language spoken or written by an individual, + enabling the recognition of distinct linguistic patterns and nuances associated + with different dialects. """ return DialectDetection(*args, asset_id=asset_id, pipeline=self, **kwargs) def inverse_text_normalization(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> InverseTextNormalization: """ - Inverse Text Normalization is the process of converting spoken or written -language in its normalized form, such as numbers, dates, and abbreviations, -back into their original, more complex or detailed textual representations. + Inverse Text Normalization is the process of converting spoken or written + language in its normalized form, such as numbers, dates, and abbreviations, + back into their original, more complex or detailed textual representations. """ return InverseTextNormalization(*args, asset_id=asset_id, pipeline=self, **kwargs) def text_to_audio(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> TextToAudio: """ - The Text to Audio function converts written text into spoken words, allowing -users to listen to the content instead of reading it. + The Text to Audio function converts written text into spoken words, allowing + users to listen to the content instead of reading it. """ return TextToAudio(*args, asset_id=asset_id, pipeline=self, **kwargs) def fill_text_mask(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> FillTextMask: """ - The "Fill Text Mask" function takes a text input with masked or placeholder -characters and replaces those placeholders with specified or contextually -appropriate characters to generate a complete and coherent text output. + The "Fill Text Mask" function takes a text input with masked or placeholder + characters and replaces those placeholders with specified or contextually + appropriate characters to generate a complete and coherent text output. """ return FillTextMask(*args, asset_id=asset_id, pipeline=self, **kwargs) def video_content_moderation(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> VideoContentModeration: """ - Video Content Moderation is the process of reviewing, analyzing, and filtering -video content to ensure it adheres to community guidelines, legal standards, -and platform policies, thereby preventing the dissemination of inappropriate, -harmful, or illegal material. + Video Content Moderation is the process of reviewing, analyzing, and filtering + video content to ensure it adheres to community guidelines, legal standards, + and platform policies, thereby preventing the dissemination of inappropriate, + harmful, or illegal material. """ return VideoContentModeration(*args, asset_id=asset_id, pipeline=self, **kwargs) def extract_audio_from_video(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> ExtractAudioFromVideo: """ - The "Extract Audio From Video" function allows users to separate and save the -audio track from a video file, enabling them to obtain just the sound without -the accompanying visual content. + The "Extract Audio From Video" function allows users to separate and save the + audio track from a video file, enabling them to obtain just the sound without + the accompanying visual content. """ return ExtractAudioFromVideo(*args, asset_id=asset_id, pipeline=self, **kwargs) def image_compression(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> ImageCompression: """ - Image compression is a process that reduces the file size of an image by -removing redundant or non-essential data, while maintaining an acceptable level -of visual quality. + Image compression is a process that reduces the file size of an image by + removing redundant or non-essential data, while maintaining an acceptable level + of visual quality. """ return ImageCompression(*args, asset_id=asset_id, pipeline=self, **kwargs) - def multilingual_speech_recognition(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> MultilingualSpeechRecognition: + def multilingual_speech_recognition( + self, asset_id: Union[str, asset.Asset], *args, **kwargs + ) -> MultilingualSpeechRecognition: """ - Multilingual Speech Recognition is a technology that enables the automatic -transcription of spoken language into text across multiple languages, allowing -for seamless communication and understanding in diverse linguistic contexts. + Multilingual Speech Recognition is a technology that enables the automatic + transcription of spoken language into text across multiple languages, allowing + for seamless communication and understanding in diverse linguistic contexts. """ return MultilingualSpeechRecognition(*args, asset_id=asset_id, pipeline=self, **kwargs) - def referenceless_text_generation_metric(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> ReferencelessTextGenerationMetric: + def referenceless_text_generation_metric( + self, asset_id: Union[str, asset.Asset], *args, **kwargs + ) -> ReferencelessTextGenerationMetric: """ - The Referenceless Text Generation Metric is a method for evaluating the quality -of generated text without requiring a reference text for comparison, often -leveraging models or algorithms to assess coherence, relevance, and fluency -based on intrinsic properties of the text itself. + The Referenceless Text Generation Metric is a method for evaluating the quality + of generated text without requiring a reference text for comparison, often + leveraging models or algorithms to assess coherence, relevance, and fluency + based on intrinsic properties of the text itself. """ return ReferencelessTextGenerationMetric(*args, asset_id=asset_id, pipeline=self, **kwargs) def text_generation_metric_default(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> TextGenerationMetricDefault: """ - The "Text Generation Metric Default" function provides a standard set of -evaluation metrics for assessing the quality and performance of text generation -models. + The "Text Generation Metric Default" function provides a standard set of + evaluation metrics for assessing the quality and performance of text generation + models. """ return TextGenerationMetricDefault(*args, asset_id=asset_id, pipeline=self, **kwargs) def noise_removal(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> NoiseRemoval: """ - Noise Removal is a process that involves identifying and eliminating unwanted -random variations or disturbances from an audio signal to enhance the clarity -and quality of the underlying information. + Noise Removal is a process that involves identifying and eliminating unwanted + random variations or disturbances from an audio signal to enhance the clarity + and quality of the underlying information. """ return NoiseRemoval(*args, asset_id=asset_id, pipeline=self, **kwargs) def audio_reconstruction(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> AudioReconstruction: """ - Audio Reconstruction is the process of restoring or recreating audio signals -from incomplete, damaged, or degraded recordings to achieve a high-quality, -accurate representation of the original sound. + Audio Reconstruction is the process of restoring or recreating audio signals + from incomplete, damaged, or degraded recordings to achieve a high-quality, + accurate representation of the original sound. """ return AudioReconstruction(*args, asset_id=asset_id, pipeline=self, **kwargs) def voice_cloning(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> VoiceCloning: """ - Voice cloning is a technology that uses artificial intelligence to create a -digital replica of a person's voice, allowing for the generation of speech that -mimics the tone, pitch, and speaking style of the original speaker. + Voice cloning is a technology that uses artificial intelligence to create a + digital replica of a person's voice, allowing for the generation of speech that + mimics the tone, pitch, and speaking style of the original speaker. """ return VoiceCloning(*args, asset_id=asset_id, pipeline=self, **kwargs) def diacritization(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> Diacritization: """ - Diacritization is the process of adding diacritical marks to letters in a text -to indicate pronunciation, stress, tone, or meaning, often used in languages -such as Arabic, Hebrew, and Vietnamese to provide clarity and accuracy in -written communication. + Diacritization is the process of adding diacritical marks to letters in a text + to indicate pronunciation, stress, tone, or meaning, often used in languages + such as Arabic, Hebrew, and Vietnamese to provide clarity and accuracy in + written communication. """ return Diacritization(*args, asset_id=asset_id, pipeline=self, **kwargs) def audio_emotion_detection(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> AudioEmotionDetection: """ - Audio Emotion Detection is a technology that analyzes vocal characteristics and -patterns in audio recordings to identify and classify the emotional state of -the speaker. + Audio Emotion Detection is a technology that analyzes vocal characteristics and + patterns in audio recordings to identify and classify the emotional state of + the speaker. """ return AudioEmotionDetection(*args, asset_id=asset_id, pipeline=self, **kwargs) def text_summarization(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> TextSummarization: """ - Text summarization is the process of condensing a large body of text into a -shorter version, capturing the main points and essential information while -maintaining coherence and meaning. + Text summarization is the process of condensing a large body of text into a + shorter version, capturing the main points and essential information while + maintaining coherence and meaning. """ return TextSummarization(*args, asset_id=asset_id, pipeline=self, **kwargs) def entity_linking(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> EntityLinking: """ - Entity Linking is the process of identifying and connecting mentions of -entities within a text to their corresponding entries in a structured knowledge -base, thereby enabling the disambiguation of terms and enhancing the -understanding of the text's context. + Entity Linking is the process of identifying and connecting mentions of + entities within a text to their corresponding entries in a structured knowledge + base, thereby enabling the disambiguation of terms and enhancing the + understanding of the text's context. """ return EntityLinking(*args, asset_id=asset_id, pipeline=self, **kwargs) def text_generation_metric(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> TextGenerationMetric: """ - A Text Generation Metric is a quantitative measure used to evaluate the quality -and effectiveness of text produced by natural language processing models, often -assessing aspects such as coherence, relevance, fluency, and adherence to given -prompts or instructions. + A Text Generation Metric is a quantitative measure used to evaluate the quality + and effectiveness of text produced by natural language processing models, often + assessing aspects such as coherence, relevance, fluency, and adherence to given + prompts or instructions. """ return TextGenerationMetric(*args, asset_id=asset_id, pipeline=self, **kwargs) def split_on_linebreak(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> SplitOnLinebreak: """ - The "Split On Linebreak" function divides a given string into a list of -substrings, using linebreaks (newline characters) as the points of separation. + The "Split On Linebreak" function divides a given string into a list of + substrings, using linebreaks (newline characters) as the points of separation. """ return SplitOnLinebreak(*args, asset_id=asset_id, pipeline=self, **kwargs) def sentiment_analysis(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> SentimentAnalysis: """ - Sentiment Analysis is a natural language processing technique used to determine -and classify the emotional tone or subjective information expressed in a piece -of text, such as identifying whether the sentiment is positive, negative, or -neutral. + Sentiment Analysis is a natural language processing technique used to determine + and classify the emotional tone or subjective information expressed in a piece + of text, such as identifying whether the sentiment is positive, negative, or + neutral. """ return SentimentAnalysis(*args, asset_id=asset_id, pipeline=self, **kwargs) def keyword_spotting(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> KeywordSpotting: """ - Keyword Spotting is a function that enables the detection and identification of -specific words or phrases within a stream of audio, often used in voice- -activated systems to trigger actions or commands based on recognized keywords. + Keyword Spotting is a function that enables the detection and identification of + specific words or phrases within a stream of audio, often used in voice- + activated systems to trigger actions or commands based on recognized keywords. """ return KeywordSpotting(*args, asset_id=asset_id, pipeline=self, **kwargs) def text_classification(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> TextClassification: """ - Text Classification is a natural language processing task that involves -categorizing text into predefined labels or classes based on its content, -enabling automated organization, filtering, and analysis of large volumes of -textual data. + Text Classification is a natural language processing task that involves + categorizing text into predefined labels or classes based on its content, + enabling automated organization, filtering, and analysis of large volumes of + textual data. """ return TextClassification(*args, asset_id=asset_id, pipeline=self, **kwargs) def other__multipurpose_(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> OtherMultipurpose: """ - The "Other (Multipurpose)" function serves as a versatile category designed to -accommodate a wide range of tasks and activities that do not fit neatly into -predefined classifications, offering flexibility and adaptability for various -needs. + The "Other (Multipurpose)" function serves as a versatile category designed to + accommodate a wide range of tasks and activities that do not fit neatly into + predefined classifications, offering flexibility and adaptability for various + needs. """ return OtherMultipurpose(*args, asset_id=asset_id, pipeline=self, **kwargs) def speech_synthesis(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> SpeechSynthesis: """ - Speech synthesis is the artificial production of human speech, typically -achieved through software or hardware systems that convert text into spoken -words, enabling machines to communicate verbally with users. + Speech synthesis is the artificial production of human speech, typically + achieved through software or hardware systems that convert text into spoken + words, enabling machines to communicate verbally with users. """ return SpeechSynthesis(*args, asset_id=asset_id, pipeline=self, **kwargs) def audio_intent_detection(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> AudioIntentDetection: """ - Audio Intent Detection is a process that involves analyzing audio signals to -identify and interpret the underlying intentions or purposes behind spoken -words, enabling systems to understand and respond appropriately to human -speech. + Audio Intent Detection is a process that involves analyzing audio signals to + identify and interpret the underlying intentions or purposes behind spoken + words, enabling systems to understand and respond appropriately to human + speech. """ return AudioIntentDetection(*args, asset_id=asset_id, pipeline=self, **kwargs) def video_label_detection(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> VideoLabelDetection: """ - Video Label Detection is a function that automatically identifies and tags -various objects, scenes, activities, and other relevant elements within a -video, providing descriptive labels that enhance searchability and content -organization. + Video Label Detection is a function that automatically identifies and tags + various objects, scenes, activities, and other relevant elements within a + video, providing descriptive labels that enhance searchability and content + organization. """ return VideoLabelDetection(*args, asset_id=asset_id, pipeline=self, **kwargs) def asr_quality_estimation(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> AsrQualityEstimation: """ - ASR Quality Estimation is a process that evaluates the accuracy and reliability -of automatic speech recognition systems by analyzing their performance in -transcribing spoken language into text. + ASR Quality Estimation is a process that evaluates the accuracy and reliability + of automatic speech recognition systems by analyzing their performance in + transcribing spoken language into text. """ return AsrQualityEstimation(*args, asset_id=asset_id, pipeline=self, **kwargs) def audio_transcript_analysis(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> AudioTranscriptAnalysis: """ - Audio Transcript Analysis is a process that involves converting spoken language -from audio recordings into written text, followed by examining and interpreting -the transcribed content to extract meaningful insights, identify patterns, and -derive actionable information. + Audio Transcript Analysis is a process that involves converting spoken language + from audio recordings into written text, followed by examining and interpreting + the transcribed content to extract meaningful insights, identify patterns, and + derive actionable information. """ return AudioTranscriptAnalysis(*args, asset_id=asset_id, pipeline=self, **kwargs) def search(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> Search: """ - The "Search" function allows users to input keywords or phrases to quickly -locate specific information, files, or content within a database, website, or -application. + The "Search" function allows users to input keywords or phrases to quickly + locate specific information, files, or content within a database, website, or + application. """ return Search(*args, asset_id=asset_id, pipeline=self, **kwargs) def video_forced_alignment(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> VideoForcedAlignment: """ - Video Forced Alignment is a process that synchronizes video footage with -corresponding audio tracks by precisely aligning the visual and auditory -elements, ensuring that the movements of speakers' lips match the spoken words. + Video Forced Alignment is a process that synchronizes video footage with + corresponding audio tracks by precisely aligning the visual and auditory + elements, ensuring that the movements of speakers' lips match the spoken words. """ return VideoForcedAlignment(*args, asset_id=asset_id, pipeline=self, **kwargs) def viseme_generation(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> VisemeGeneration: """ - Viseme Generation is the process of creating visual representations of -phonemes, which are the distinct units of sound in speech, to synchronize lip -movements with spoken words in animations or virtual avatars. + Viseme Generation is the process of creating visual representations of + phonemes, which are the distinct units of sound in speech, to synchronize lip + movements with spoken words in animations or virtual avatars. """ return VisemeGeneration(*args, asset_id=asset_id, pipeline=self, **kwargs) def topic_classification(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> TopicClassification: """ - Topic Classification is a natural language processing function that categorizes -text into predefined topics or subjects based on its content, enabling -efficient organization and retrieval of information. + Topic Classification is a natural language processing function that categorizes + text into predefined topics or subjects based on its content, enabling + efficient organization and retrieval of information. """ return TopicClassification(*args, asset_id=asset_id, pipeline=self, **kwargs) - def offensive_language_identification(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> OffensiveLanguageIdentification: + def offensive_language_identification( + self, asset_id: Union[str, asset.Asset], *args, **kwargs + ) -> OffensiveLanguageIdentification: """ - Offensive Language Identification is a function that analyzes text to detect -and flag language that is abusive, harmful, or inappropriate, helping to -maintain a respectful and safe communication environment. + Offensive Language Identification is a function that analyzes text to detect + and flag language that is abusive, harmful, or inappropriate, helping to + maintain a respectful and safe communication environment. """ return OffensiveLanguageIdentification(*args, asset_id=asset_id, pipeline=self, **kwargs) def speech_translation(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> SpeechTranslation: """ - Speech Translation is a technology that converts spoken language in real-time -from one language to another, enabling seamless communication between speakers -of different languages. + Speech Translation is a technology that converts spoken language in real-time + from one language to another, enabling seamless communication between speakers + of different languages. """ return SpeechTranslation(*args, asset_id=asset_id, pipeline=self, **kwargs) def speaker_diarization_audio(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> SpeakerDiarizationAudio: """ - Speaker Diarization Audio is a process that involves segmenting an audio -recording into distinct sections, each corresponding to a different speaker, in -order to identify and differentiate between multiple speakers within the same -audio stream. + Speaker Diarization Audio is a process that involves segmenting an audio + recording into distinct sections, each corresponding to a different speaker, in + order to identify and differentiate between multiple speakers within the same + audio stream. """ return SpeakerDiarizationAudio(*args, asset_id=asset_id, pipeline=self, **kwargs) def audio_transcript_improvement(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> AudioTranscriptImprovement: """ - Audio Transcript Improvement is a function that enhances the accuracy and -clarity of transcribed audio recordings by correcting errors, refining -language, and ensuring the text faithfully represents the original spoken -content. + Audio Transcript Improvement is a function that enhances the accuracy and + clarity of transcribed audio recordings by correcting errors, refining + language, and ensuring the text faithfully represents the original spoken + content. """ return AudioTranscriptImprovement(*args, asset_id=asset_id, pipeline=self, **kwargs) - def speech_non_speech_classification(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> SpeechNonSpeechClassification: + def speech_non_speech_classification( + self, asset_id: Union[str, asset.Asset], *args, **kwargs + ) -> SpeechNonSpeechClassification: """ - The function "Speech or Non-Speech Classification" is designed to analyze audio -input and determine whether the sound is human speech or non-speech noise, -enabling applications such as voice recognition systems to filter out -irrelevant background sounds. + The function "Speech or Non-Speech Classification" is designed to analyze audio + input and determine whether the sound is human speech or non-speech noise, + enabling applications such as voice recognition systems to filter out + irrelevant background sounds. """ return SpeechNonSpeechClassification(*args, asset_id=asset_id, pipeline=self, **kwargs) def text_denormalization(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> TextDenormalization: """ - Text Denormalization is the process of converting abbreviated, contracted, or -otherwise simplified text into its full, standard form, often to improve -readability and ensure consistency in natural language processing tasks. + Text Denormalization is the process of converting abbreviated, contracted, or + otherwise simplified text into its full, standard form, often to improve + readability and ensure consistency in natural language processing tasks. """ return TextDenormalization(*args, asset_id=asset_id, pipeline=self, **kwargs) def image_content_moderation(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> ImageContentModeration: """ - Image Content Moderation is a process that involves analyzing and filtering -images to detect and manage inappropriate, harmful, or sensitive content, -ensuring compliance with community guidelines and legal standards. + Image Content Moderation is a process that involves analyzing and filtering + images to detect and manage inappropriate, harmful, or sensitive content, + ensuring compliance with community guidelines and legal standards. """ return ImageContentModeration(*args, asset_id=asset_id, pipeline=self, **kwargs) - def referenceless_text_generation_metric_default(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> ReferencelessTextGenerationMetricDefault: + def referenceless_text_generation_metric_default( + self, asset_id: Union[str, asset.Asset], *args, **kwargs + ) -> ReferencelessTextGenerationMetricDefault: """ - The Referenceless Text Generation Metric Default is a function designed to -evaluate the quality of generated text without relying on reference texts for -comparison. + The Referenceless Text Generation Metric Default is a function designed to + evaluate the quality of generated text without relying on reference texts for + comparison. """ return ReferencelessTextGenerationMetricDefault(*args, asset_id=asset_id, pipeline=self, **kwargs) def named_entity_recognition(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> NamedEntityRecognition: """ - Named Entity Recognition (NER) is a natural language processing task that -involves identifying and classifying proper nouns in text into predefined -categories such as names of people, organizations, locations, dates, and other -entities. + Named Entity Recognition (NER) is a natural language processing task that + involves identifying and classifying proper nouns in text into predefined + categories such as names of people, organizations, locations, dates, and other + entities. """ return NamedEntityRecognition(*args, asset_id=asset_id, pipeline=self, **kwargs) def text_content_moderation(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> TextContentModeration: """ - Text Content Moderation is the process of reviewing, filtering, and managing -user-generated content to ensure it adheres to community guidelines, legal -standards, and platform policies, thereby maintaining a safe and respectful -online environment. + Text Content Moderation is the process of reviewing, filtering, and managing + user-generated content to ensure it adheres to community guidelines, legal + standards, and platform policies, thereby maintaining a safe and respectful + online environment. """ return TextContentModeration(*args, asset_id=asset_id, pipeline=self, **kwargs) def speaker_diarization_video(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> SpeakerDiarizationVideo: """ - The Speaker Diarization Video function identifies and segments different -speakers in a video, attributing portions of the audio to individual speakers -to facilitate analysis and understanding of multi-speaker conversations. + The Speaker Diarization Video function identifies and segments different + speakers in a video, attributing portions of the audio to individual speakers + to facilitate analysis and understanding of multi-speaker conversations. """ return SpeakerDiarizationVideo(*args, asset_id=asset_id, pipeline=self, **kwargs) def split_on_silence(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> SplitOnSilence: """ - The "Split On Silence" function divides an audio recording into separate -segments based on periods of silence, allowing for easier editing and analysis -of individual sections. + The "Split On Silence" function divides an audio recording into separate + segments based on periods of silence, allowing for easier editing and analysis + of individual sections. """ return SplitOnSilence(*args, asset_id=asset_id, pipeline=self, **kwargs) def emotion_detection(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> EmotionDetection: """ - Emotion Detection is a process that involves analyzing text to identify and -categorize the emotional states or sentiments expressed by individuals, such as -happiness, sadness, anger, or fear. + Emotion Detection is a process that involves analyzing text to identify and + categorize the emotional states or sentiments expressed by individuals, such as + happiness, sadness, anger, or fear. """ return EmotionDetection(*args, asset_id=asset_id, pipeline=self, **kwargs) def text_spam_detection(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> TextSpamDetection: """ - Text Spam Detection is a process that involves analyzing and identifying -unsolicited or irrelevant messages within text communications, typically using -algorithms and machine learning techniques to filter out spam and ensure the -integrity of the communication platform. + Text Spam Detection is a process that involves analyzing and identifying + unsolicited or irrelevant messages within text communications, typically using + algorithms and machine learning techniques to filter out spam and ensure the + integrity of the communication platform. """ return TextSpamDetection(*args, asset_id=asset_id, pipeline=self, **kwargs) def translation(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> Translation: """ - Translation is the process of converting text from one language into an -equivalent text in another language, preserving the original meaning and -context. + Translation is the process of converting text from one language into an + equivalent text in another language, preserving the original meaning and + context. """ return Translation(*args, asset_id=asset_id, pipeline=self, **kwargs) def voice_activity_detection(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> VoiceActivityDetection: """ - Voice Activity Detection (VAD) is a technology that identifies the presence or -absence of human speech within an audio signal, enabling systems to distinguish -between spoken words and background noise. + Voice Activity Detection (VAD) is a technology that identifies the presence or + absence of human speech within an audio signal, enabling systems to distinguish + between spoken words and background noise. """ return VoiceActivityDetection(*args, asset_id=asset_id, pipeline=self, **kwargs) def speech_embedding(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> SpeechEmbedding: """ - Speech Embedding is a process that transforms spoken language into a fixed- -dimensional vector representation, capturing essential features and -characteristics of the speech for tasks such as recognition, classification, -and analysis. + Speech Embedding is a process that transforms spoken language into a fixed- + dimensional vector representation, capturing essential features and + characteristics of the speech for tasks such as recognition, classification, + and analysis. """ return SpeechEmbedding(*args, asset_id=asset_id, pipeline=self, **kwargs) def subtitling_translation(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> SubtitlingTranslation: """ - Subtitling Translation is the process of converting spoken dialogue from one -language into written text in another language, which is then displayed on- -screen to aid viewers in understanding the content. + Subtitling Translation is the process of converting spoken dialogue from one + language into written text in another language, which is then displayed on- + screen to aid viewers in understanding the content. """ return SubtitlingTranslation(*args, asset_id=asset_id, pipeline=self, **kwargs) def text_generation(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> TextGeneration: """ - Text Generation is a process in which artificial intelligence models, such as -neural networks, produce coherent and contextually relevant text based on a -given input or prompt, often mimicking human writing styles and patterns. + Text Generation is a process in which artificial intelligence models, such as + neural networks, produce coherent and contextually relevant text based on a + given input or prompt, often mimicking human writing styles and patterns. """ return TextGeneration(*args, asset_id=asset_id, pipeline=self, **kwargs) def video_understanding(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> VideoUnderstanding: """ - Video Understanding is the process of analyzing and interpreting video content -to extract meaningful information, such as identifying objects, actions, -events, and contextual relationships within the footage. + Video Understanding is the process of analyzing and interpreting video content + to extract meaningful information, such as identifying objects, actions, + events, and contextual relationships within the footage. """ return VideoUnderstanding(*args, asset_id=asset_id, pipeline=self, **kwargs) def text_to_video_generation(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> TextToVideoGeneration: """ - Text To Video Generation is a process that converts written descriptions or -scripts into dynamic, visual video content using advanced algorithms and -artificial intelligence. + Text To Video Generation is a process that converts written descriptions or + scripts into dynamic, visual video content using advanced algorithms and + artificial intelligence. """ return TextToVideoGeneration(*args, asset_id=asset_id, pipeline=self, **kwargs) def text_normalization(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> TextNormalization: """ - Text normalization is the process of transforming text into a standard, -consistent format by correcting spelling errors, converting all characters to a -uniform case, removing punctuation, and expanding abbreviations to improve the -text's readability and usability for further processing or analysis. + Text normalization is the process of transforming text into a standard, + consistent format by correcting spelling errors, converting all characters to a + uniform case, removing punctuation, and expanding abbreviations to improve the + text's readability and usability for further processing or analysis. """ return TextNormalization(*args, asset_id=asset_id, pipeline=self, **kwargs) def speech_recognition(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> SpeechRecognition: """ - Speech recognition is a technology that enables a computer or device to -identify and process spoken language, converting it into text. + Speech recognition is a technology that enables a computer or device to + identify and process spoken language, converting it into text. """ return SpeechRecognition(*args, asset_id=asset_id, pipeline=self, **kwargs) def subtitling(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> Subtitling: """ - Subtitling is the process of displaying written text on a screen to represent -the spoken dialogue, narration, or other audio elements in a video, typically -to aid viewers who are deaf or hard of hearing, or to provide translations for -audiences who speak different languages. + Subtitling is the process of displaying written text on a screen to represent + the spoken dialogue, narration, or other audio elements in a video, typically + to aid viewers who are deaf or hard of hearing, or to provide translations for + audiences who speak different languages. """ return Subtitling(*args, asset_id=asset_id, pipeline=self, **kwargs) def classification_metric(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> ClassificationMetric: """ - A Classification Metric is a quantitative measure used to evaluate the quality -and effectiveness of classification models. + A Classification Metric is a quantitative measure used to evaluate the quality + and effectiveness of classification models. """ return ClassificationMetric(*args, asset_id=asset_id, pipeline=self, **kwargs) def text_to_image_generation(self, asset_id: Union[str, asset.Asset], *args, **kwargs) -> TextToImageGeneration: """ - Text To Image Generation is a process where a system creates visual images -based on descriptive text input, translating written language into -corresponding graphical representations. + Text To Image Generation is a process where a system creates visual images + based on descriptive text input, translating written language into + corresponding graphical representations. """ return TextToImageGeneration(*args, asset_id=asset_id, pipeline=self, **kwargs) - diff --git a/aixplain/modules/team_agent/__init__.py b/aixplain/modules/team_agent/__init__.py index 86321489..2f8b5c3b 100644 --- a/aixplain/modules/team_agent/__init__.py +++ b/aixplain/modules/team_agent/__init__.py @@ -25,8 +25,10 @@ import logging import time import traceback +import re from aixplain.utils.file_utils import _request_with_retry +from aixplain.enums.function import Function from aixplain.enums.supplier import Supplier from aixplain.enums.asset_status import AssetStatus from aixplain.enums.storage_type import StorageType @@ -259,3 +261,68 @@ def delete(self) -> None: ) logging.error(message) raise Exception(f"{message}") + + def to_dict(self) -> Dict: + return { + "id": self.id, + "name": self.name, + "agents": [ + {"assetId": agent.id, "number": idx, "type": "AGENT", "label": "AGENT"} for idx, agent in enumerate(self.agents) + ], + "links": [], + "description": self.description, + "llmId": self.llm_id, + "supervisorId": self.llm_id, + "plannerId": self.llm_id if self.use_mentalist_and_inspector else None, + "supplier": self.supplier, + "version": self.version, + } + + def validate(self) -> None: + """Validate the Team.""" + from aixplain.factories.model_factory import ModelFactory + + # validate name + assert ( + re.match("^[a-zA-Z0-9 ]*$", self.name) is not None + ), "Team Agent Creation Error: Team name must not contain special characters." + + try: + llm = ModelFactory.get(self.llm_id) + assert llm.function == Function.TEXT_GENERATION, "Large Language Model must be a text generation model." + except Exception: + raise Exception(f"Large Language Model with ID '{self.llm_id}' not found.") + + for agent in self.agents: + agent.validate() + + def update(self) -> None: + """Update the Team Agent.""" + from aixplain.factories.team_agent_factory.utils import build_team_agent + + self.validate() + url = urljoin(config.BACKEND_URL, f"sdk/agent-communities/{self.id}") + headers = {"x-api-key": config.TEAM_API_KEY, "Content-Type": "application/json"} + + payload = self.to_dict() + + logging.debug(f"Start service for PUT Update Team Agent - {url} - {headers} - {json.dumps(payload)}") + resp = "No specified error." + try: + r = _request_with_retry("put", url, headers=headers, json=payload) + resp = r.json() + except Exception: + raise Exception("Team Agent Update Error: Please contact the administrators.") + + if 200 <= r.status_code < 300: + return build_team_agent(resp) + else: + error_msg = f"Team Agent Update Error (HTTP {r.status_code}): {resp}" + raise Exception(error_msg) + + def deploy(self) -> None: + """Deploy the Team Agent.""" + assert self.status == AssetStatus.DRAFT, "Team Agent Deployment Error: Team Agent must be in draft status." + assert self.status != AssetStatus.ONBOARDED, "Team Agent Deployment Error: Team Agent must be onboarded." + self.status = AssetStatus.ONBOARDED + self.update() diff --git a/aixplain/modules/wallet.py b/aixplain/modules/wallet.py index 2b2b1cd4..e05e2d3a 100644 --- a/aixplain/modules/wallet.py +++ b/aixplain/modules/wallet.py @@ -32,4 +32,4 @@ def __init__(self, total_balance: float, reserved_balance: float): """ self.total_balance = total_balance self.reserved_balance = reserved_balance - self.available_balance = total_balance-reserved_balance \ No newline at end of file + self.available_balance = total_balance - reserved_balance diff --git a/aixplain/utils/__init__.py b/aixplain/utils/__init__.py index 8e82233f..552d61e3 100644 --- a/aixplain/utils/__init__.py +++ b/aixplain/utils/__init__.py @@ -18,4 +18,4 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -""" \ No newline at end of file +""" diff --git a/aixplain/utils/convert_datatype_utils.py b/aixplain/utils/convert_datatype_utils.py index 4d9f321f..00dff186 100644 --- a/aixplain/utils/convert_datatype_utils.py +++ b/aixplain/utils/convert_datatype_utils.py @@ -14,12 +14,12 @@ limitations under the License. """ -from typing import Union, Dict, List +from typing import Union, Dict, List from aixplain.modules.metadata import MetaData def dict_to_metadata(metadatas: List[Union[Dict, MetaData]]) -> None: - + """Convert all the Dicts to MetaData Args: @@ -32,5 +32,3 @@ def dict_to_metadata(metadatas: List[Union[Dict, MetaData]]) -> None: metadatas[i] = MetaData(**metadatas[i]) except TypeError: raise TypeError(f"Data Asset Onboarding Error: One or more elements in the metadata_schema are not well-structured") - - \ No newline at end of file diff --git a/docs/streaming/aixplain_diarization_streaming_client.py b/docs/streaming/aixplain_diarization_streaming_client.py index 799c63a7..6689c855 100644 --- a/docs/streaming/aixplain_diarization_streaming_client.py +++ b/docs/streaming/aixplain_diarization_streaming_client.py @@ -25,6 +25,7 @@ FRAME_RATE = 16000 + def generate_payloads(file_path, latency): stream_configuration = pb.DiarizationRequest( config=pb.AudioConfig(encoding="LINEAR16", hertz=FRAME_RATE, language_code="en"), @@ -33,60 +34,68 @@ def generate_payloads(file_path, latency): ) yield stream_configuration # Iterate over the raw bytes in chunks - chunk_size = 16000 # half a second of audio + chunk_size = 16000 # half a second of audio i = 0 with open(file_path, "rb") as audio_file: while True: chunk = audio_file.read(chunk_size) if not chunk: break - logging.info(f'Sending chunk {i}') + logging.info(f"Sending chunk {i}") payload = pb.DiarizationRequest(audio_content=chunk) yield payload i += 1 - time.sleep(0.5) # simulate streaming by introducing sleep + time.sleep(0.5) # simulate streaming by introducing sleep + def grpc_duration_to_seconds(duration): seconds = float(duration.seconds) nanos = float(duration.nanos) / 1e9 return seconds + nanos + def consume_results(response: List[pb.DiarizationResponse]): for inference in response: if inference.is_final: - logging.info(f'Received is_final={inference.is_final}. total_time={inference.end_time.seconds}.{str(inference.end_time.nanos)[:3]}') + logging.info( + f"Received is_final={inference.is_final}. total_time={inference.end_time.seconds}.{str(inference.end_time.nanos)[:3]}" + ) if len(inference.segments): - logging.info(f'Turns:') + logging.info(f"Turns:") for segment in inference.segments: - logging.info(f"{segment.speaker_tag} \ - start:{grpc_duration_to_seconds(segment.start_time)}\tend:{grpc_duration_to_seconds(segment.end_time)}") + logging.info( + f"{segment.speaker_tag} \ + start:{grpc_duration_to_seconds(segment.start_time)}\tend:{grpc_duration_to_seconds(segment.end_time)}" + ) + def _stream_file(channel, file_path, latency): stub = aixplain_diarization_streaming_pb2_grpc.AixplainDiarizationStreamingStub(channel) response = stub.Diarize(generate_payloads(file_path, latency)) consume_results(response) + def run_insecure(host, file_path, latency): - with grpc.insecure_channel(host, options=(('grpc.ssl_target_name_override', host),)) as channel: + with grpc.insecure_channel(host, options=(("grpc.ssl_target_name_override", host),)) as channel: _stream_file(channel, file_path, latency) + def run(host, client_cert, client_key, ca_cert, file_path, latency): def create_secure_channel(host, client_cert, client_key, ca_cert): - with open(client_cert, 'rb') as f: + with open(client_cert, "rb") as f: client_cert_data = f.read() - with open(client_key, 'rb') as f: + with open(client_key, "rb") as f: client_key_data = f.read() - with open(ca_cert, 'rb') as f: + with open(ca_cert, "rb") as f: ca_cert_data = f.read() credentials = grpc.ssl_channel_credentials( - root_certificates=ca_cert_data, - private_key=client_key_data, - certificate_chain=client_cert_data + root_certificates=ca_cert_data, private_key=client_key_data, certificate_chain=client_cert_data ) return grpc.secure_channel(host, credentials) + with create_secure_channel(host, client_cert, client_key, ca_cert) as channel: _stream_file(channel, file_path, latency) @@ -96,13 +105,13 @@ def create_secure_channel(host, client_cert, client_key, ca_cert): logging.basicConfig(level=logging.INFO, format=log_format) parser = argparse.ArgumentParser(description="aiXplain speech recognition streaming client.") - parser.add_argument('--addr', default='localhost:50051', help='the address to connect to (default "localhost:50051")') - parser.add_argument('--cacert', default='./client-crt/ca.crt', help='ca cert for mTLS (default "./client-crt/ca.crt")') - parser.add_argument('--cert', default='./client-crt/tls.crt', help='client cert for mTLS (default "./client-crt/tls.crt")') - parser.add_argument('--key', default='./client-crt/tls.key', help='client key for mTLS (default "./client-crt/tls.key")') - parser.add_argument('--insecure', action='store_true', help='use insecure connection (no mTLS)') - parser.add_argument('--file-path', help='audio file to stream from') - parser.add_argument('--latency', type=float, help='Model latency') + parser.add_argument("--addr", default="localhost:50051", help='the address to connect to (default "localhost:50051")') + parser.add_argument("--cacert", default="./client-crt/ca.crt", help='ca cert for mTLS (default "./client-crt/ca.crt")') + parser.add_argument("--cert", default="./client-crt/tls.crt", help='client cert for mTLS (default "./client-crt/tls.crt")') + parser.add_argument("--key", default="./client-crt/tls.key", help='client key for mTLS (default "./client-crt/tls.key")') + parser.add_argument("--insecure", action="store_true", help="use insecure connection (no mTLS)") + parser.add_argument("--file-path", help="audio file to stream from") + parser.add_argument("--latency", type=float, help="Model latency") args = parser.parse_args() diff --git a/docs/streaming/aixplain_speech_transcription_streaming_client.py b/docs/streaming/aixplain_speech_transcription_streaming_client.py index 97f4e5dc..87fb3a8a 100644 --- a/docs/streaming/aixplain_speech_transcription_streaming_client.py +++ b/docs/streaming/aixplain_speech_transcription_streaming_client.py @@ -25,19 +25,21 @@ import aixplain_speech_transcription_streaming_pb2_grpc FRAME_RATE = 16000 -FFMPEG_FORMAT = 'wav' +FFMPEG_FORMAT = "wav" -PS_SUBTITLE = 'subtitle' -PS_LOG = 'log' +PS_SUBTITLE = "subtitle" +PS_LOG = "log" RED = "\033[0;31m" GREEN = "\033[0;32m" DEFAULT = "\033[0;97m" + def grpc_duration_to_seconds(duration): seconds = float(duration.seconds) nanos = float(duration.nanos) / 1e9 return seconds + nanos + def generate_payloads(file_path): # uncomment this if your audio file is not compatible stream_configuration = pb.SpeechRecognitionRequest( @@ -45,78 +47,87 @@ def generate_payloads(file_path): ) yield stream_configuration # Iterate over the raw bytes in chunks - chunk_size = 16000 # half a second of audio + chunk_size = 16000 # half a second of audio chunk_period = 0.5 - logging.info(f'Sending chunks...') + logging.info(f"Sending chunks...") with open(file_path, "rb") as audio_file: i = 0 while True: chunk = audio_file.read(chunk_size) if not chunk: break - time.sleep((len(chunk)/chunk_size)*chunk_period) # simulate streaming by introducing sleep - logging.debug(f'Sending chunk {i}') + time.sleep((len(chunk) / chunk_size) * chunk_period) # simulate streaming by introducing sleep + logging.debug(f"Sending chunk {i}") payload = pb.SpeechRecognitionRequest(audio_content=chunk) yield payload i += 1 + def consume_results(response: List[pb.SpeechRecognitionResponse], print_style): for i, inference in enumerate(response): if i == 0: - logging.info(f'Detected language {inference.language_code=}') + logging.info(f"Detected language {inference.language_code=}") if len(inference.hypotheses): # get the top hypothesis hypothesis = inference.hypotheses[0] transcript, confidence = hypothesis.transcript, hypothesis.confidence if inference.is_final: - logging.info(f't={grpc_duration_to_seconds(inference.end_time):.3f} conf={confidence:.2f} FINAL="{transcript}"') + logging.info( + f't={grpc_duration_to_seconds(inference.end_time):.3f} conf={confidence:.2f} FINAL="{transcript}"' + ) if print_style == PS_SUBTITLE: sys.stdout.write(GREEN) sys.stdout.write("\033[K") - sys.stdout.write(f'{grpc_duration_to_seconds(inference.end_time):.3f}' + ": " + transcript + "\n") + sys.stdout.write(f"{grpc_duration_to_seconds(inference.end_time):.3f}" + ": " + transcript + "\n") else: - logging.info(f't={grpc_duration_to_seconds(inference.end_time):.3f} conf={confidence:.2f} chunk="{transcript}"') + logging.info( + f't={grpc_duration_to_seconds(inference.end_time):.3f} conf={confidence:.2f} chunk="{transcript}"' + ) if print_style == PS_SUBTITLE: sys.stdout.write(RED) sys.stdout.write("\033[K") - sys.stdout.write(f'{grpc_duration_to_seconds(inference.end_time):.3f}' + ": " + transcript + "\r") + sys.stdout.write(f"{grpc_duration_to_seconds(inference.end_time):.3f}" + ": " + transcript + "\r") for word in hypothesis.words: - logging.info(f'Word: {word.word.ljust(12)} ' - f'Start-End: {grpc_duration_to_seconds(word.start_time):.3f}-{grpc_duration_to_seconds(word.end_time):.3f} ' - f'Confidence: {word.confidence:.3f} ') + logging.info( + f"Word: {word.word.ljust(12)} " + f"Start-End: {grpc_duration_to_seconds(word.start_time):.3f}-{grpc_duration_to_seconds(word.end_time):.3f} " + f"Confidence: {word.confidence:.3f} " + ) else: # called at the end - logging.info(f'{inference.is_final=} server processing_time={grpc_duration_to_seconds(inference.end_time):.3f}') + logging.info(f"{inference.is_final=} server processing_time={grpc_duration_to_seconds(inference.end_time):.3f}") if print_style == PS_SUBTITLE: sys.stdout.write(DEFAULT) sys.stdout.write("Exiting...\n") + def _stream_file(channel, file_path, print_style): stub = aixplain_speech_transcription_streaming_pb2_grpc.AixplainSpeechStreamingStub(channel) response = stub.SpeechRecognize(generate_payloads(file_path)) consume_results(response, print_style) + def run_insecure(host, file_path, print_style): - with grpc.insecure_channel(host, options=(('grpc.ssl_target_name_override', host),)) as channel: + with grpc.insecure_channel(host, options=(("grpc.ssl_target_name_override", host),)) as channel: _stream_file(channel, file_path, print_style) + def run(host, client_cert, client_key, ca_cert, file_path, print_style): def create_secure_channel(host, client_cert, client_key, ca_cert): - with open(client_cert, 'rb') as f: + with open(client_cert, "rb") as f: client_cert_data = f.read() - with open(client_key, 'rb') as f: + with open(client_key, "rb") as f: client_key_data = f.read() - with open(ca_cert, 'rb') as f: + with open(ca_cert, "rb") as f: ca_cert_data = f.read() credentials = grpc.ssl_channel_credentials( - root_certificates=ca_cert_data, - private_key=client_key_data, - certificate_chain=client_cert_data + root_certificates=ca_cert_data, private_key=client_key_data, certificate_chain=client_cert_data ) return grpc.secure_channel(host, credentials) + with create_secure_channel(host, client_cert, client_key, ca_cert) as channel: _stream_file(channel, file_path, print_style) @@ -126,20 +137,22 @@ def create_secure_channel(host, client_cert, client_key, ca_cert): logging.basicConfig(level=logging.INFO, format=log_format) parser = argparse.ArgumentParser(description="aiXplain speech recognition streaming client.") - parser.add_argument('--print-style', default='log', choices=[PS_SUBTITLE, PS_LOG], help='The print style, either "log" or "subtitle"') - parser.add_argument('--addr', default='localhost:50051', help='the address to connect to (default "localhost:50051")') - parser.add_argument('--cacert', default='./client-crt/ca.crt', help='ca cert for mTLS (default "./client-crt/ca.crt")') - parser.add_argument('--cert', default='./client-crt/tls.crt', help='client cert for mTLS (default "./client-crt/tls.crt")') - parser.add_argument('--key', default='./client-crt/tls.key', help='client key for mTLS (default "./client-crt/tls.key")') - parser.add_argument('--insecure', action='store_true', help='use insecure connection (no mTLS)') - parser.add_argument('--file-path', default="resources/conv.wav", help='audio file to stream from') + parser.add_argument( + "--print-style", default="log", choices=[PS_SUBTITLE, PS_LOG], help='The print style, either "log" or "subtitle"' + ) + parser.add_argument("--addr", default="localhost:50051", help='the address to connect to (default "localhost:50051")') + parser.add_argument("--cacert", default="./client-crt/ca.crt", help='ca cert for mTLS (default "./client-crt/ca.crt")') + parser.add_argument("--cert", default="./client-crt/tls.crt", help='client cert for mTLS (default "./client-crt/tls.crt")') + parser.add_argument("--key", default="./client-crt/tls.key", help='client key for mTLS (default "./client-crt/tls.key")') + parser.add_argument("--insecure", action="store_true", help="use insecure connection (no mTLS)") + parser.add_argument("--file-path", default="resources/conv.wav", help="audio file to stream from") args = parser.parse_args() if args.print_style == PS_SUBTITLE: - logging.getLogger('').setLevel(logging.ERROR) + logging.getLogger("").setLevel(logging.ERROR) if args.insecure: run_insecure(args.addr, args.file_path, args.print_style) else: - run(args.addr, args.cert, args.key, args.cacert, args.file_path, args.print_style) + run(args.addr, args.cert, args.key, args.cacert, args.file_path, args.print_style) diff --git a/docs/streaming/make_audio_compatible.py b/docs/streaming/make_audio_compatible.py index 9d04c2d5..d9a43277 100644 --- a/docs/streaming/make_audio_compatible.py +++ b/docs/streaming/make_audio_compatible.py @@ -4,6 +4,7 @@ FRAME_RATE = 16000 + def create_compatible_audio(source_path, dest_path): """ Function to resample an audio file and change the number of channels if there are more than 1. @@ -13,22 +14,23 @@ def create_compatible_audio(source_path, dest_path): updated = False if sound_file.frame_rate != FRAME_RATE: # Resample the audio file - logging.info(f'Resampling {sound_file.frame_rate} -> {FRAME_RATE}') + logging.info(f"Resampling {sound_file.frame_rate} -> {FRAME_RATE}") sound_file = sound_file.set_frame_rate(FRAME_RATE) updated = True # If the audio file has more than one channel, convert it to mono if sound_file.channels > 1: - logging.info(f'Changing no. channels {sound_file.channels} -> 1') + logging.info(f"Changing no. channels {sound_file.channels} -> 1") sound_file = sound_file.set_channels(1) updated = True if updated: # Export the processed audio file sound_file.export(dest_path, format="wav") + if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Process some audio files.') - parser.add_argument('--source_path', required=True, help='Source path for the audio file') - parser.add_argument('--dest_path', required=True, help='Destination path for the processed audio file') + parser = argparse.ArgumentParser(description="Process some audio files.") + parser.add_argument("--source_path", required=True, help="Source path for the audio file") + parser.add_argument("--dest_path", required=True, help="Destination path for the processed audio file") args = parser.parse_args() diff --git a/tests/__init__.py b/tests/__init__.py index 8e82233f..552d61e3 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -18,4 +18,4 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -""" \ No newline at end of file +""" diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..a03eea30 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,4 @@ +from dotenv import load_dotenv + +# Load environment variables once for all tests +load_dotenv() diff --git a/tests/functional/agent/agent_functional_test.py b/tests/functional/agent/agent_functional_test.py index 648f4f28..55d671e0 100644 --- a/tests/functional/agent/agent_functional_test.py +++ b/tests/functional/agent/agent_functional_test.py @@ -15,13 +15,16 @@ See the License for the specific language governing permissions and limitations under the License. """ +import copy import json from dotenv import load_dotenv load_dotenv() -from aixplain.factories import AgentFactory +from aixplain.factories import AgentFactory, TeamAgentFactory +from aixplain.enums.asset_status import AssetStatus from aixplain.enums.function import Function from aixplain.enums.supplier import Supplier +from uuid import uuid4 import pytest @@ -37,21 +40,35 @@ def run_input_map(request): return request.param -def test_end2end(run_input_map): +@pytest.fixture(scope="function") +def delete_agents_and_team_agents(): + for team_agent in TeamAgentFactory.list()["results"]: + team_agent.delete() for agent in AgentFactory.list()["results"]: agent.delete() + yield True + + for team_agent in TeamAgentFactory.list()["results"]: + team_agent.delete() + for agent in AgentFactory.list()["results"]: + agent.delete() + + +def test_end2end(run_input_map, delete_agents_and_team_agents): + assert delete_agents_and_team_agents tools = [] if "model_tools" in run_input_map: for tool in run_input_map["model_tools"]: + tool_ = copy.copy(tool) for supplier in Supplier: if tool["supplier"] is not None and tool["supplier"].lower() in [ supplier.value["code"].lower(), supplier.value["name"].lower(), ]: - tool["supplier"] = supplier + tool_["supplier"] = supplier break - tools.append(AgentFactory.create_model_tool(**tool)) + tools.append(AgentFactory.create_model_tool(**tool_)) if "pipeline_tools" in run_input_map: for tool in run_input_map["pipeline_tools"]: tools.append(AgentFactory.create_pipeline_tool(pipeline=tool["pipeline_id"], description=tool["description"])) @@ -60,6 +77,11 @@ def test_end2end(run_input_map): name=run_input_map["agent_name"], description=run_input_map["agent_name"], llm_id=run_input_map["llm_id"], tools=tools ) assert agent is not None + assert agent.status == AssetStatus.DRAFT + # deploy agent + agent.deploy() + assert agent.status == AssetStatus.ONBOARDED + agent = AgentFactory.get(agent.id) assert agent is not None response = agent.run(data=run_input_map["query"]) @@ -79,7 +101,41 @@ def test_list_agents(): assert type(agents_result) is list -def test_fail_non_existent_llm(): +def test_update_draft_agent(run_input_map, delete_agents_and_team_agents): + assert delete_agents_and_team_agents + + tools = [] + if "model_tools" in run_input_map: + for tool in run_input_map["model_tools"]: + tool_ = copy.copy(tool) + for supplier in Supplier: + if tool["supplier"] is not None and tool["supplier"].lower() in [ + supplier.value["code"].lower(), + supplier.value["name"].lower(), + ]: + tool_["supplier"] = supplier + break + tools.append(AgentFactory.create_model_tool(**tool_)) + if "pipeline_tools" in run_input_map: + for tool in run_input_map["pipeline_tools"]: + tools.append(AgentFactory.create_pipeline_tool(pipeline=tool["pipeline_id"], description=tool["description"])) + + agent = AgentFactory.create( + name=run_input_map["agent_name"], description=run_input_map["agent_name"], llm_id=run_input_map["llm_id"], tools=tools + ) + + agent_name = str(uuid4()).replace("-", "") + agent.name = agent_name + agent.update() + + agent = AgentFactory.get(agent.id) + assert agent.name == agent_name + assert agent.status == AssetStatus.DRAFT + agent.delete() + + +def test_fail_non_existent_llm(delete_agents_and_team_agents): + assert delete_agents_and_team_agents with pytest.raises(Exception) as exc_info: AgentFactory.create( name="Test Agent", @@ -88,3 +144,22 @@ def test_fail_non_existent_llm(): tools=[AgentFactory.create_model_tool(function=Function.TRANSLATION)], ) assert str(exc_info.value) == "Large Language Model with ID 'non_existent_llm' not found." + + +def test_delete_agent_in_use(delete_agents_and_team_agents): + assert delete_agents_and_team_agents + agent = AgentFactory.create( + name="Test Agent", + description="Test description", + tools=[AgentFactory.create_model_tool(function=Function.TRANSLATION)], + ) + TeamAgentFactory.create( + name="Test Team Agent", + agents=[agent], + description="Test description", + use_mentalist_and_inspector=True, + ) + + with pytest.raises(Exception) as exc_info: + agent.delete() + assert str(exc_info.value) == "Agent Deletion Error (HTTP 403): err.agent_is_in_use." diff --git a/tests/functional/apikey/test_api.py b/tests/functional/apikey/test_api.py index 221a58fb..2c228f6b 100644 --- a/tests/functional/apikey/test_api.py +++ b/tests/functional/apikey/test_api.py @@ -199,62 +199,6 @@ def test_list_update_api_keys(): break - -def test_list_update_api_keys(): - api_keys = APIKeyFactory.list() - assert isinstance(api_keys, list) - - for api_key in api_keys: - assert isinstance(api_key, APIKey) - assert api_key.id != "" - - from random import randint - - number = randint(0, 10000) - if api_key.global_limits is None: - api_key.global_limits = APIKeyGlobalLimits( - token_per_minute=number, - token_per_day=number, - request_per_day=number, - request_per_minute=number, - ) - else: - api_key.global_limits.token_per_day = number - api_key.global_limits.token_per_minute = number - api_key.global_limits.request_per_day = number - api_key.global_limits.request_per_minute = number - - if api_key.asset_limits is None: - api_key.asset_limits = [] - - if len(api_key.asset_limits) == 0: - api_key.asset_limits.append( - APIKeyGlobalLimits( - model="640b517694bf816d35a59125", - token_per_minute=number, - token_per_day=number, - request_per_day=number, - request_per_minute=number, - ) - ) - else: - api_key.asset_limits[0].request_per_day = number - api_key.asset_limits[0].request_per_minute = number - api_key.asset_limits[0].token_per_day = number - api_key.asset_limits[0].token_per_minute = number - api_key = APIKeyFactory.update(api_key) - - assert api_key.global_limits.token_per_day == number - assert api_key.global_limits.token_per_minute == number - assert api_key.global_limits.request_per_day == number - assert api_key.global_limits.request_per_minute == number - assert api_key.asset_limits[0].request_per_day == number - assert api_key.asset_limits[0].request_per_minute == number - assert api_key.asset_limits[0].token_per_day == number - assert api_key.asset_limits[0].token_per_minute == number - break - - def test_create_api_key_wrong_input(): api_key_name = "Test API Key" diff --git a/tests/functional/benchmark/benchmark_functional_test.py b/tests/functional/benchmark/benchmark_functional_test.py index ef8b77b8..51bbb74d 100644 --- a/tests/functional/benchmark/benchmark_functional_test.py +++ b/tests/functional/benchmark/benchmark_functional_test.py @@ -35,10 +35,11 @@ def run_input_map(request): def module_input_map(request): return request.param + def is_job_finshed(benchmark_job): time_taken = 0 sleep_time = 15 - timeout = 10 * 60 + timeout = 15 * 60 while True: if time_taken > timeout: break @@ -52,15 +53,15 @@ def is_job_finshed(benchmark_job): break return False + def assert_correct_results(benchmark_job): df = benchmark_job.download_results_as_csv(return_dataframe=True) assert type(df) is pd.DataFrame, "Couldn't download CSV" - model_success_rate = (sum(df["Model_success"])*100)/len(df.index) - assert model_success_rate > 80 , f"Low model success rate ({model_success_rate})" + model_success_rate = (sum(df["Model_success"]) * 100) / len(df.index) + assert model_success_rate > 80, f"Low model success rate ({model_success_rate})" metric_name = "BLEU by sacrebleu" mean_score = df[metric_name].mean() - assert mean_score != 0 , f"Zero Mean Score - Please check metric ({metric_name})" - + assert mean_score != 0, f"Zero Mean Score - Please check metric ({metric_name})" def test_create_and_run(run_input_map): @@ -75,9 +76,6 @@ def test_create_and_run(run_input_map): assert_correct_results(benchmark_job) - - - # def test_module(module_input_map): # benchmark = BenchmarkFactory.get(module_input_map["benchmark_id"]) # assert benchmark.id == module_input_map["benchmark_id"] diff --git a/tests/functional/file_asset/file_create_test.py b/tests/functional/file_asset/file_create_test.py index adbf24b6..6d2a5739 100644 --- a/tests/functional/file_asset/file_create_test.py +++ b/tests/functional/file_asset/file_create_test.py @@ -20,22 +20,14 @@ from aixplain.enums import License from aixplain.factories import FileFactory + def test_file_create(): upload_file = "tests/functional/file_asset/input/test.csv" - s3_link = FileFactory.create( - local_path = upload_file, - tags = ['test1','test2'], - license = License.MIT, - is_temp = False - ) + s3_link = FileFactory.create(local_path=upload_file, tags=["test1", "test2"], license=License.MIT, is_temp=False) assert s3_link.startswith("s3") + def test_file_create_temp(): upload_file = "tests/functional/file_asset/input/test.csv" - s3_link = FileFactory.create( - local_path = upload_file, - tags = ['test1','test2'], - license = License.MIT, - is_temp = True - ) + s3_link = FileFactory.create(local_path=upload_file, tags=["test1", "test2"], license=License.MIT, is_temp=True) assert s3_link.startswith("s3") diff --git a/tests/functional/model/image_upload_e2e_test.py b/tests/functional/model/image_upload_e2e_test.py index 7c7efbcc..90ebddfd 100644 --- a/tests/functional/model/image_upload_e2e_test.py +++ b/tests/functional/model/image_upload_e2e_test.py @@ -38,7 +38,9 @@ def test_create_and_upload_model(): input_modality = mock_register_payload["input_modality"] output_modality = mock_register_payload["output_modality"] documentation_url = mock_register_payload["documentation_url"] - register_response = ModelFactory.create_asset_repo(name, description, function, source_language, input_modality, output_modality, documentation_url, config.TEAM_API_KEY) + register_response = ModelFactory.create_asset_repo( + name, description, function, source_language, input_modality, output_modality, documentation_url, config.TEAM_API_KEY + ) assert "id" in register_response.keys() assert "repositoryName" in register_response.keys() model_id = register_response["id"] diff --git a/tests/functional/model/image_upload_functional_test.py b/tests/functional/model/image_upload_functional_test.py index 60d1d3f0..cae7b731 100644 --- a/tests/functional/model/image_upload_functional_test.py +++ b/tests/functional/model/image_upload_functional_test.py @@ -7,6 +7,7 @@ import docker import pytest + def test_login(): response = ModelFactory.asset_repo_login() assert response["username"] == "AWS" @@ -27,7 +28,9 @@ def test_create_asset_repo(): input_modality = mock_register_payload["input_modality"] output_modality = mock_register_payload["output_modality"] documentation_url = mock_register_payload["documentation_url"] - response = ModelFactory.create_asset_repo(name, description, function, source_language, input_modality, output_modality, documentation_url, config.TEAM_API_KEY) + response = ModelFactory.create_asset_repo( + name, description, function, source_language, input_modality, output_modality, documentation_url, config.TEAM_API_KEY + ) response_dict = dict(response) assert "id" in response_dict.keys() assert "repositoryName" in response_dict.keys() diff --git a/tests/functional/pipelines/run_test.py b/tests/functional/pipelines/run_test.py index 25fadaf4..6ca9e6fe 100644 --- a/tests/functional/pipelines/run_test.py +++ b/tests/functional/pipelines/run_test.py @@ -51,7 +51,7 @@ def test_get_pipeline(): def test_run_single_str(batchmode: bool, version: str): pipeline = PipelineFactory.list(query="SingleNodePipeline")["results"][0] - response = pipeline.run(data="Translate this thing", **{"batchmode": batchmode, "version": version}) + response = pipeline.run(data="Translate this thing", batch_mode=batchmode, **{"version": version}) assert response["status"] == "SUCCESS" @@ -71,7 +71,7 @@ def test_run_single_local_file(batchmode: bool, version: str): with open(fname, "w") as f: f.write("Translate this thing") - response = pipeline.run(data=fname, **{"batchmode": batchmode, "version": version}) + response = pipeline.run(data=fname, batch_mode=batchmode, **{"version": version}) os.remove(fname) assert response["status"] == "SUCCESS" @@ -90,7 +90,8 @@ def test_run_with_url(batchmode: bool, version: str): response = pipeline.run( data="https://aixplain-platform-assets.s3.amazonaws.com/data/dev/64c81163f8bdcac7443c2dad/data/f8.txt", - **{"batchmode": batchmode, "version": version} + batch_mode=batchmode, + **{"version": version} ) assert response["status"] == "SUCCESS" @@ -110,7 +111,7 @@ def test_run_with_dataset(batchmode: bool, version: str): data_id = dataset.source_data["en"].id pipeline = PipelineFactory.list(query="SingleNodePipeline")["results"][0] - response = pipeline.run(data=data_id, data_asset=data_asset_id, **{"batchmode": batchmode, "version": version}) + response = pipeline.run(data=data_id, data_asset=data_asset_id, batch_mode=batchmode, **{"version": version}) assert response["status"] == "SUCCESS" @@ -128,7 +129,8 @@ def test_run_multipipe_with_strings(batchmode: bool, version: str): response = pipeline.run( data={"Input": "Translate this thing.", "Reference": "Traduza esta coisa."}, - **{"batchmode": batchmode, "version": version} + batch_mode=batchmode, + **{"version": version} ) assert response["status"] == "SUCCESS" @@ -154,7 +156,8 @@ def test_run_multipipe_with_datasets(batchmode: bool, version: str): response = pipeline.run( data={"Input": input_id, "Reference": reference_id}, data_asset={"Input": data_asset_id, "Reference": data_asset_id}, - **{"batchmode": batchmode, "version": version} + batch_mode=batchmode, + **{"version": version} ) assert response["status"] == "SUCCESS" @@ -238,3 +241,13 @@ def test_run_decision(input_data: str, output_data: str, version: str): assert response["status"] == "SUCCESS" assert response["data"][0]["label"] == output_data + + +@pytest.mark.parametrize("version", ["3.0"]) +def test_run_script(version: str): + pipeline = PipelineFactory.list(query="Script Functional Test - DO NOT DELETE")["results"][0] + response = pipeline.run("https://aixplain-platform-assets.s3.amazonaws.com/samples/en/CPAC1x2.wav", **{"version": version}) + + assert response["status"] == "SUCCESS" + data = response["data"][0]["segments"][0]["response"] + assert data.startswith("SCRIPT MODIFIED:") diff --git a/tests/functional/team_agent/team_agent_functional_test.py b/tests/functional/team_agent/team_agent_functional_test.py index c7e50b68..44ea5dbc 100644 --- a/tests/functional/team_agent/team_agent_functional_test.py +++ b/tests/functional/team_agent/team_agent_functional_test.py @@ -20,9 +20,11 @@ load_dotenv() from aixplain.factories import AgentFactory, TeamAgentFactory +from aixplain.enums.asset_status import AssetStatus from aixplain.enums.function import Function from aixplain.enums.supplier import Supplier - +from copy import copy +from uuid import uuid4 import pytest RUN_FILE = "tests/functional/team_agent/data/team_agent_test_end2end.json" @@ -31,29 +33,43 @@ def read_data(data_path): return json.load(open(data_path, "r")) +@pytest.fixture(scope="function") +def delete_agents_and_team_agents(): + for team_agent in TeamAgentFactory.list()["results"]: + team_agent.delete() + for agent in AgentFactory.list()["results"]: + agent.delete() + + yield True + + for team_agent in TeamAgentFactory.list()["results"]: + team_agent.delete() + for agent in AgentFactory.list()["results"]: + agent.delete() + @pytest.fixture(scope="module", params=read_data(RUN_FILE)) def run_input_map(request): return request.param -def test_end2end(run_input_map): - for agent in AgentFactory.list()["results"]: - agent.delete() +def test_end2end(run_input_map, delete_agents_and_team_agents): + assert delete_agents_and_team_agents agents = [] for agent in run_input_map["agents"]: tools = [] if "model_tools" in agent: for tool in agent["model_tools"]: + tool_ = copy(tool) for supplier in Supplier: if tool["supplier"] is not None and tool["supplier"].lower() in [ supplier.value["code"].lower(), supplier.value["name"].lower(), ]: - tool["supplier"] = supplier + tool_["supplier"] = supplier break - tools.append(AgentFactory.create_model_tool(**tool)) + tools.append(AgentFactory.create_model_tool(**tool_)) if "pipeline_tools" in agent: for tool in agent["pipeline_tools"]: tools.append(AgentFactory.create_pipeline_tool(pipeline=tool["pipeline_id"], description=tool["description"])) @@ -61,6 +77,7 @@ def test_end2end(run_input_map): agent = AgentFactory.create( name=agent["agent_name"], description=agent["agent_name"], llm_id=agent["llm_id"], tools=tools ) + agent.deploy() agents.append(agent) team_agent = TeamAgentFactory.create( @@ -72,6 +89,9 @@ def test_end2end(run_input_map): ) assert team_agent is not None + assert team_agent.status == AssetStatus.DRAFT + # deploy team agent + team_agent.deploy() team_agent = TeamAgentFactory.get(team_agent.id) assert team_agent is not None response = team_agent.run(data=run_input_map["query"]) @@ -86,6 +106,51 @@ def test_end2end(run_input_map): team_agent.delete() +def test_draft_team_agent_update(run_input_map): + for team in TeamAgentFactory.list()["results"]: + team.delete() + for agent in AgentFactory.list()["results"]: + agent.delete() + + agents = [] + for agent in run_input_map["agents"]: + tools = [] + if "model_tools" in agent: + for tool in agent["model_tools"]: + tool_ = copy(tool) + for supplier in Supplier: + if tool["supplier"] is not None and tool["supplier"].lower() in [ + supplier.value["code"].lower(), + supplier.value["name"].lower(), + ]: + tool_["supplier"] = supplier + break + tools.append(AgentFactory.create_model_tool(**tool_)) + if "pipeline_tools" in agent: + for tool in agent["pipeline_tools"]: + tools.append(AgentFactory.create_pipeline_tool(pipeline=tool["pipeline_id"], description=tool["description"])) + + agent = AgentFactory.create( + name=agent["agent_name"], description=agent["agent_name"], llm_id=agent["llm_id"], tools=tools + ) + agents.append(agent) + + team_agent = TeamAgentFactory.create( + name=run_input_map["team_agent_name"], + agents=agents, + description=run_input_map["team_agent_name"], + llm_id=run_input_map["llm_id"], + use_mentalist_and_inspector=True, + ) + + team_agent_name = str(uuid4()).replace("-", "") + team_agent.name = team_agent_name + team_agent.update() + team_agent = TeamAgentFactory.get(team_agent.id) + assert team_agent.name == team_agent_name + assert team_agent.status == AssetStatus.DRAFT + + def test_fail_non_existent_llm(): with pytest.raises(Exception) as exc_info: AgentFactory.create( diff --git a/tests/test_utils.py b/tests/test_utils.py index e7a41e16..264538d5 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -3,14 +3,16 @@ import logging from aixplain.utils import config + def delete_asset(model_id, api_key): delete_url = urljoin(config.BACKEND_URL, f"sdk/models/{model_id}") logging.debug(f"URL: {delete_url}") headers = {"Authorization": f"Token {api_key}", "Content-Type": "application/json"} _ = _request_with_retry("delete", delete_url, headers=headers) + def delete_service_account(api_key): delete_url = urljoin(config.BACKEND_URL, f"sdk/ecr/logout") logging.debug(f"URL: {delete_url}") headers = {"Authorization": f"Token {api_key}", "Content-Type": "application/json"} - _ = _request_with_retry("post", delete_url, headers=headers) \ No newline at end of file + _ = _request_with_retry("post", delete_url, headers=headers) diff --git a/tests/unit/agent_test.py b/tests/unit/agent_test.py index 1be0682e..1b4fd929 100644 --- a/tests/unit/agent_test.py +++ b/tests/unit/agent_test.py @@ -1,5 +1,6 @@ import pytest import requests_mock +from aixplain.enums.asset_status import AssetStatus from aixplain.modules import Agent from aixplain.utils import config from aixplain.factories import AgentFactory @@ -53,7 +54,7 @@ def test_fail_key_not_found(): assert str(exc_info.value) == "Key 'input2' not found in query." -def test_sucess_query_content(): +def test_success_query_content(): agent = Agent("123", "Test Agent", "Sample Description") with requests_mock.Mocker() as mock: url = agent.url @@ -83,6 +84,12 @@ def test_invalid_modeltool(): assert str(exc_info.value) == "Model Tool Unavailable. Make sure Model '309851793' exists or you have access to it." +def test_invalid_llm_id(): + with pytest.raises(Exception) as exc_info: + AgentFactory.create(name="Test", description="", tools=[], llm_id="123") + assert str(exc_info.value) == "Large Language Model with ID '123' not found." + + def test_invalid_agent_name(): with pytest.raises(Exception) as exc_info: AgentFactory.create(name="[Test]", description="", tools=[], llm_id="6646261c6eb563165658bbb1") @@ -102,7 +109,7 @@ def test_create_agent(): "description": "Test Agent Description", "teamId": "123", "version": "1.0", - "status": "onboarded", + "status": "draft", "llmId": "6646261c6eb563165658bbb1", "pricing": {"currency": "USD", "value": 0.0}, "assets": [ @@ -112,6 +119,7 @@ def test_create_agent(): "version": "1.0", "assetId": "6646261c6eb563165658bbb1", "function": "text-generation", + "description": "Test Tool", } ], } @@ -134,10 +142,87 @@ def test_create_agent(): name="Test Agent", description="Test Agent Description", llm_id="6646261c6eb563165658bbb1", - tools=[AgentFactory.create_model_tool(supplier=Supplier.OPENAI, function="text-generation")], + tools=[ + AgentFactory.create_model_tool(supplier=Supplier.OPENAI, function="text-generation", description="Test Tool") + ], ) assert agent.name == ref_response["name"] assert agent.description == ref_response["description"] assert agent.llm_id == ref_response["llmId"] assert agent.tools[0].function.value == ref_response["assets"][0]["function"] + assert agent.tools[0].description == ref_response["assets"][0]["description"] + assert agent.status == AssetStatus.DRAFT + + +def test_to_dict(): + agent = Agent( + id="", + name="Test Agent", + description="Test Agent Description", + llm_id="6646261c6eb563165658bbb1", + tools=[AgentFactory.create_model_tool(function="text-generation")], + ) + + agent_json = agent.to_dict() + assert agent_json["id"] == "" + assert agent_json["name"] == "Test Agent" + assert agent_json["description"] == "Test Agent Description" + assert agent_json["llmId"] == "6646261c6eb563165658bbb1" + assert agent_json["assets"][0]["function"] == "text-generation" + assert agent_json["assets"][0]["type"] == "model" + + +def test_update_success(): + agent = Agent( + id="123", + name="Test Agent", + description="Test Agent Description", + llm_id="6646261c6eb563165658bbb1", + tools=[AgentFactory.create_model_tool(function="text-generation")], + ) + + with requests_mock.Mocker() as mock: + url = urljoin(config.BACKEND_URL, f"sdk/agents/{agent.id}") + headers = {"x-api-key": config.TEAM_API_KEY, "Content-Type": "application/json"} + ref_response = { + "id": "123", + "name": "Test Agent", + "description": "Test Agent Description", + "teamId": "123", + "version": "1.0", + "status": "onboarded", + "llmId": "6646261c6eb563165658bbb1", + "pricing": {"currency": "USD", "value": 0.0}, + "assets": [ + { + "type": "model", + "supplier": "openai", + "version": "1.0", + "assetId": "6646261c6eb563165658bbb1", + "function": "text-generation", + } + ], + } + mock.put(url, headers=headers, json=ref_response) + + url = urljoin(config.BACKEND_URL, "sdk/models/6646261c6eb563165658bbb1") + model_ref_response = { + "id": "6646261c6eb563165658bbb1", + "name": "Test LLM", + "description": "Test LLM Description", + "function": {"id": "text-generation"}, + "supplier": "openai", + "version": {"id": "1.0"}, + "status": "onboarded", + "pricing": {"currency": "USD", "value": 0.0}, + } + mock.get(url, headers=headers, json=model_ref_response) + + agent.update() + + assert agent.id == ref_response["id"] + assert agent.name == ref_response["name"] + assert agent.description == ref_response["description"] + assert agent.llm_id == ref_response["llmId"] + assert agent.tools[0].function.value == ref_response["assets"][0]["function"] diff --git a/tests/unit/designer_unit_test.py b/tests/unit/designer_unit_test.py index 824fd162..57276a20 100644 --- a/tests/unit/designer_unit_test.py +++ b/tests/unit/designer_unit_test.py @@ -1,6 +1,5 @@ import pytest -import unittest.mock as mock - +from unittest.mock import patch, Mock, call from aixplain.enums import DataType from aixplain.modules.pipeline.designer.base import ( @@ -21,7 +20,7 @@ from aixplain.modules.pipeline.designer.mixins import LinkableMixin from aixplain.modules.pipeline.designer.pipeline import DesignerPipeline - +from aixplain.modules.pipeline.designer.base import find_prompt_params def test_create_node(): @@ -30,7 +29,7 @@ def test_create_node(): class BareNode(Node): pass - with mock.patch("aixplain.modules.pipeline.designer.Node.attach_to") as mock_attach_to: + with patch("aixplain.modules.pipeline.designer.Node.attach_to") as mock_attach_to: node = BareNode(number=3, label="FOO") mock_attach_to.assert_not_called() assert isinstance(node.inputs, Inputs) @@ -48,7 +47,7 @@ class FooNode(Node[FooNodeInputs, FooNodeOutputs]): inputs_class = FooNodeInputs outputs_class = FooNodeOutputs - with mock.patch("aixplain.modules.pipeline.designer.Node.attach_to") as mock_attach_to: + with patch("aixplain.modules.pipeline.designer.Node.attach_to") as mock_attach_to: node = FooNode(pipeline=pipeline, number=3, label="FOO") mock_attach_to.assert_called_once_with(pipeline) assert isinstance(node.inputs, FooNodeInputs) @@ -115,8 +114,8 @@ class AssetNode(Node): node = AssetNode() - with mock.patch.object(node.inputs, "serialize") as mock_inputs_serialize: - with mock.patch.object(node.outputs, "serialize") as mock_outputs_serialize: + with patch.object(node.inputs, "serialize") as mock_inputs_serialize: + with patch.object(node.outputs, "serialize") as mock_outputs_serialize: assert node.serialize() == { "number": node.number, "type": NodeType.ASSET, @@ -145,7 +144,7 @@ def test_create_param(): class TypedParam(Param): param_type = ParamType.INPUT - with mock.patch("aixplain.modules.pipeline.designer.Param.attach_to") as mock_attach_to: + with patch("aixplain.modules.pipeline.designer.Param.attach_to") as mock_attach_to: param = TypedParam( code="param", data_type=DataType.TEXT, @@ -158,7 +157,7 @@ class TypedParam(Param): assert param.value == "foo" assert param.param_type == ParamType.INPUT - with mock.patch("aixplain.modules.pipeline.designer.Param.attach_to") as mock_attach_to: + with patch("aixplain.modules.pipeline.designer.Param.attach_to") as mock_attach_to: param = TypedParam( code="param", data_type=DataType.TEXT, @@ -175,7 +174,7 @@ class TypedParam(Param): class UnTypedParam(Param): pass - with mock.patch("aixplain.modules.pipeline.designer.Param.attach_to") as mock_attach_to: + with patch("aixplain.modules.pipeline.designer.Param.attach_to") as mock_attach_to: param = UnTypedParam( code="param", data_type=DataType.TEXT, @@ -186,7 +185,7 @@ class UnTypedParam(Param): assert param.param_type == ParamType.OUTPUT - with mock.patch("aixplain.modules.pipeline.designer.Param.attach_to") as mock_attach_to: + with patch("aixplain.modules.pipeline.designer.Param.attach_to") as mock_attach_to: param = UnTypedParam( code="param", data_type=DataType.TEXT, @@ -202,7 +201,7 @@ class AssetNode(Node): node = AssetNode() - with mock.patch("aixplain.modules.pipeline.designer.Param.attach_to") as mock_attach_to: + with patch("aixplain.modules.pipeline.designer.Param.attach_to") as mock_attach_to: param = UnTypedParam( code="param", data_type=DataType.TEXT, @@ -226,7 +225,7 @@ class AssetNode(Node): node = AssetNode() - with mock.patch("aixplain.modules.pipeline.designer.Param.attach_to") as mock_attach_to: + with patch("aixplain.modules.pipeline.designer.Param.attach_to") as mock_attach_to: param = param_cls(code="param", data_type=DataType.TEXT, value="foo", node=node) mock_attach_to.assert_called_once_with(node) assert param.code == "param" @@ -253,7 +252,7 @@ class NoTypeParam(Param): input = InputParam(code="input", data_type=DataType.TEXT, value="foo") - with mock.patch.object(node.inputs, "add_param") as mock_add_param: + with patch.object(node.inputs, "add_param") as mock_add_param: input.attach_to(node) mock_add_param.assert_called_once_with(input) assert input.node is node @@ -265,7 +264,7 @@ class NoTypeParam(Param): output = OutputParam(code="output", data_type=DataType.TEXT, value="bar") - with mock.patch.object(node.outputs, "add_param") as mock_add_param: + with patch.object(node.outputs, "add_param") as mock_add_param: output.attach_to(node) mock_add_param.assert_called_once_with(output) assert output.node is node @@ -304,7 +303,7 @@ class AssetNode(Node, LinkableMixin): output = OutputParam(code="output", data_type=DataType.TEXT, value="bar", node=a) input = InputParam(code="input", data_type=DataType.TEXT, value="foo", node=b) - with mock.patch.object(input, "back_link") as mock_back_link: + with patch.object(input, "back_link") as mock_back_link: output.link(input) mock_back_link.assert_called_once_with(output) @@ -342,7 +341,7 @@ class AssetNode(Node, LinkableMixin): output = OutputParam(code="output", data_type=DataType.TEXT, value="bar", node=a) input = InputParam(code="input", data_type=DataType.TEXT, value="foo", node=b) - with mock.patch.object(a, "link") as mock_link: + with patch.object(a, "link") as mock_link: input.back_link(output) mock_link.assert_called_once_with(b, output, input) @@ -400,7 +399,7 @@ class AssetNode(Node, LinkableMixin): pipeline = DesignerPipeline() - with mock.patch("aixplain.modules.pipeline.designer.Link.attach_to") as mock_attach_to: + with patch("aixplain.modules.pipeline.designer.Link.attach_to") as mock_attach_to: link = Link( from_node=a, to_node=b, @@ -431,8 +430,8 @@ class AssetNode(Node, LinkableMixin): to_param="input", ) - with mock.patch.object(a, "attach_to") as mock_a_attach_to: - with mock.patch.object(b, "attach_to") as mock_b_attach_to: + with patch.object(a, "attach_to") as mock_a_attach_to: + with patch.object(b, "attach_to") as mock_b_attach_to: link.attach_to(pipeline) mock_a_attach_to.assert_called_once_with(pipeline) mock_b_attach_to.assert_called_once_with(pipeline) @@ -451,8 +450,8 @@ class AssetNode(Node, LinkableMixin): to_param="input", ) - with mock.patch.object(a, "attach_to") as mock_a_attach_to: - with mock.patch.object(b, "attach_to") as mock_b_attach_to: + with patch.object(a, "attach_to") as mock_a_attach_to: + with patch.object(b, "attach_to") as mock_b_attach_to: link.attach_to(pipeline) mock_a_attach_to.assert_not_called() mock_b_attach_to.assert_not_called() @@ -555,8 +554,8 @@ class AssetNode(Node): param_proxy = ParamProxy(node) - with mock.patch.object(param_proxy, "_create_param") as mock_create_param: - with mock.patch.object(param_proxy, "add_param") as mock_add_param: + with patch.object(param_proxy, "_create_param") as mock_create_param: + with patch.object(param_proxy, "add_param") as mock_add_param: param = param_proxy.create_param("foo", DataType.TEXT, "bar", is_required=True) mock_create_param.assert_called_once_with("foo", DataType.TEXT, "bar") mock_add_param.assert_called_once_with(param) @@ -588,6 +587,48 @@ class FooParam(Param): assert "'bar'" in str(excinfo.value) +def test_param_proxy_set_param_value(): + prompt_param = Mock(spec=Param, code="prompt") + param_proxy = ParamProxy(Mock()) + param_proxy._params = [prompt_param] + with patch.object(param_proxy, "special_prompt_handling") as mock_special_prompt_handling: + param_proxy.set_param_value("prompt", "hello {{foo}}") + mock_special_prompt_handling.assert_called_once_with("prompt", "hello {{foo}}") + assert prompt_param.value == "hello {{foo}}" + + +def test_param_proxy_special_prompt_handling(): + from aixplain.modules.pipeline.designer.nodes import AssetNode + + asset_node = Mock(spec=AssetNode, asset=Mock(function="text-generation")) + param_proxy = ParamProxy(asset_node) + with patch( + "aixplain.modules.pipeline.designer.base.find_prompt_params" + ) as mock_find_prompt_params: + mock_find_prompt_params.return_value = [] + param_proxy.special_prompt_handling("prompt", "hello {{foo}}") + mock_find_prompt_params.assert_called_once_with("hello {{foo}}") + asset_node.inputs.create_param.assert_not_called() + asset_node.reset_mock() + mock_find_prompt_params.reset_mock() + + mock_find_prompt_params.return_value = ["foo"] + param_proxy.special_prompt_handling("prompt", "hello {{foo}}") + mock_find_prompt_params.assert_called_once_with("hello {{foo}}") + asset_node.inputs.create_param.assert_called_once_with("foo", DataType.TEXT, is_required=True) + asset_node.reset_mock() + mock_find_prompt_params.reset_mock() + + mock_find_prompt_params.return_value = ["foo", "bar"] + param_proxy.special_prompt_handling("prompt", "hello {{foo}} {{bar}}") + mock_find_prompt_params.assert_called_once_with("hello {{foo}} {{bar}}") + assert asset_node.inputs.create_param.call_count == 2 + assert asset_node.inputs.create_param.call_args_list == [ + call("foo", DataType.TEXT, is_required=True), + call("bar", DataType.TEXT, is_required=True), + ] + + def test_node_link(): class AssetNode(Node, LinkableMixin): type: NodeType = NodeType.ASSET @@ -623,7 +664,7 @@ class AssetNode(Node): type: NodeType = NodeType.ASSET node1 = AssetNode() - with mock.patch.object(node1, "attach_to") as mock_attach_to: + with patch.object(node1, "attach_to") as mock_attach_to: pipeline.add_node(node1) mock_attach_to.assert_called_once_with(pipeline) @@ -636,14 +677,14 @@ class InputNode(Node): node = InputNode() - with mock.patch.object(pipeline, "add_node") as mock_add_node: + with patch.object(pipeline, "add_node") as mock_add_node: pipeline.add_nodes(node) assert mock_add_node.call_count == 1 node1 = InputNode() node2 = InputNode() - with mock.patch.object(pipeline, "add_node") as mock_add_node: + with patch.object(pipeline, "add_node") as mock_add_node: pipeline.add_nodes(node1, node2) assert mock_add_node.call_count == 2 @@ -662,6 +703,95 @@ class AssetNode(Node): link = Link(from_node=a, to_node=b, from_param="output", to_param="input") pipeline.add_link(link) - with mock.patch.object(link, "attach_to") as mock_attach_to: + with patch.object(link, "attach_to") as mock_attach_to: pipeline.add_link(link) mock_attach_to.assert_called_once_with(pipeline) + + +def test_pipeline_special_prompt_validation(): + from aixplain.modules.pipeline.designer.nodes import AssetNode + + pipeline = DesignerPipeline() + asset_node = Mock( + spec=AssetNode, + label="LLM(ID=1)", + asset=Mock(function="text-generation"), + inputs=Mock(prompt=Mock(value="hello {{foo}}"), text=Mock(is_required=True)), + ) + with patch.object(pipeline, "is_param_set") as mock_is_param_set: + mock_is_param_set.return_value = False + pipeline.special_prompt_validation(asset_node) + mock_is_param_set.assert_called_once_with(asset_node, asset_node.inputs.prompt) + assert asset_node.inputs.text.is_required is True + mock_is_param_set.reset_mock() + mock_is_param_set.return_value = True + with patch( + "aixplain.modules.pipeline.designer.pipeline.find_prompt_params" + ) as mock_find_prompt_params: + mock_find_prompt_params.return_value = [] + pipeline.special_prompt_validation(asset_node) + mock_is_param_set.assert_called_once_with( + asset_node, asset_node.inputs.prompt + ) + mock_find_prompt_params.assert_called_once_with( + asset_node.inputs.prompt.value + ) + assert asset_node.inputs.text.is_required is True + + mock_is_param_set.reset_mock() + mock_is_param_set.return_value = True + mock_find_prompt_params.reset_mock() + mock_find_prompt_params.return_value = ["foo"] + asset_node.inputs.__contains__ = Mock(return_value=False) + + with pytest.raises( + ValueError, + match="Param foo of node LLM\\(ID=1\\) should be defined and set", + ): + pipeline.special_prompt_validation(asset_node) + + mock_is_param_set.assert_called_once_with( + asset_node, asset_node.inputs.prompt + ) + mock_find_prompt_params.assert_called_once_with( + asset_node.inputs.prompt.value + ) + assert asset_node.inputs.text.is_required is False + + mock_is_param_set.reset_mock() + mock_is_param_set.return_value = True + mock_find_prompt_params.reset_mock() + mock_find_prompt_params.return_value = ["foo"] + asset_node.inputs.text.is_required = True + + asset_node.inputs.__contains__ = Mock(return_value=True) + pipeline.special_prompt_validation(asset_node) + mock_is_param_set.assert_called_once_with( + asset_node, asset_node.inputs.prompt + ) + mock_find_prompt_params.assert_called_once_with( + asset_node.inputs.prompt.value + ) + assert asset_node.inputs.text.is_required is False + + +@pytest.mark.parametrize( + "input, expected", + [ + ("hello {{foo}}", ["foo"]), + ("hello {{foo}} {{bar}}", ["foo", "bar"]), + ("hello {{foo}} {{bar}} {{baz}}", ["foo", "bar", "baz"]), + # no match cases + ("hello bar", []), + ("hello {{foo]] bar", []), + ("hello {foo} bar", []), + # edge cases + ("", []), + ("{{}}", []), + # interesting cases + ("hello {{foo {{bar}} baz}} {{bar}} {{baz}}", ["foo {{bar", "bar", "baz"]), + ], +) +def test_find_prompt_params(input, expected): + print(input, expected) + assert find_prompt_params(input) == expected diff --git a/tests/unit/finetune_test.py b/tests/unit/finetune_test.py index 13287c32..3691bd40 100644 --- a/tests/unit/finetune_test.py +++ b/tests/unit/finetune_test.py @@ -68,6 +68,7 @@ def test_create(): assert finetune.model.id == test_model assert finetune.cost.to_dict() == cost_estimation_map + def test_create_exception(): model_map = read_data(MODEL_FILE) with requests_mock.Mocker() as mock: @@ -109,14 +110,15 @@ def test_start(): assert fine_tuned_model is not None assert fine_tuned_model.id == model_map["id"] + @pytest.mark.parametrize( - "input_path,after_epoch,training_loss,validation_loss", + "input_path,after_epoch,training_loss,validation_loss", [ - (FINETUNE_STATUS_FILE, None, 0.4, 0.0217), + (FINETUNE_STATUS_FILE, None, 0.4, 0.0217), (FINETUNE_STATUS_FILE, 1, 0.2, 0.0482), - (FINETUNE_STATUS_FILE_2, None, 2.657801408034, 2.596168756485), - (FINETUNE_STATUS_FILE_2, 0, None, 2.684150457382) - ] + (FINETUNE_STATUS_FILE_2, None, 2.657801408034, 2.596168756485), + (FINETUNE_STATUS_FILE_2, 0, None, 2.684150457382), + ], ) def test_check_finetuner_status(input_path, after_epoch, training_loss, validation_loss): model_map = read_data(input_path) @@ -146,4 +148,4 @@ def test_list_finetunable_models(is_finetunable): model_list = result["results"] assert len(model_list) > 0 for model_index in range(len(model_list)): - assert model_list[model_index].id == list_map["items"][model_index]["id"] \ No newline at end of file + assert model_list[model_index].id == list_map["items"][model_index]["id"] diff --git a/tests/unit/image_upload_test.py b/tests/unit/image_upload_test.py index 4b192292..7cd43946 100644 --- a/tests/unit/image_upload_test.py +++ b/tests/unit/image_upload_test.py @@ -56,6 +56,7 @@ def test_list_host_machines(): for key in machine_dict.keys(): assert machine_dict[key] == mock_json_dict[key] + def test_get_functions(): url = urljoin(config.BACKEND_URL, f"sdk/functions") with requests_mock.Mocker() as mock: @@ -65,6 +66,7 @@ def test_get_functions(): functions = ModelFactory.list_functions(config.TEAM_API_KEY) assert functions == mock_json + @pytest.mark.skip(reason="Not currently supported.") def test_list_image_repo_tags(): model_id = "mock_id" diff --git a/tests/unit/llm_test.py b/tests/unit/llm_test.py index 54887950..753a8f7a 100644 --- a/tests/unit/llm_test.py +++ b/tests/unit/llm_test.py @@ -4,6 +4,8 @@ load_dotenv() from aixplain.utils import config +from aixplain.enums import ResponseStatus +from aixplain.modules.model.response import ModelResponse from aixplain.modules import LLM import pytest @@ -49,3 +51,110 @@ def test_run_async_errors(status_code, error_message): response = test_llm.run_async(data="input_data") assert response["status"] == "FAILED" assert response["error_message"] == error_message + + +def test_run_sync(): + model_id = "test-model-id" + base_url = config.MODELS_RUN_URL + execute_url = f"{base_url}/{model_id}".replace("/api/v1/execute", "/api/v2/execute") + + ref_response = { + "status": "IN_PROGRESS", + "data": "https://models.aixplain.com/api/v1/data/a90c2078-edfe-403f-acba-d2d94cf71f42", + } + + poll_response = { + "completed": True, + "status": "SUCCESS", + "data": "Test Model Result", + "usedCredits": 0, + "runTime": 0, + } + + with requests_mock.Mocker() as mock: + mock.post(execute_url, json=ref_response) + + poll_url = ref_response["data"] + mock.get(poll_url, json=poll_response) + + test_model = LLM( + id=model_id, name="Test Model", function=Function.TEXT_GENERATION, url=base_url, api_key=config.TEAM_API_KEY + ) + + input_data = {"data": "input_data"} + response = test_model.run(data=input_data, temperature=0.001, max_tokens=128, top_p=1.0) + + assert isinstance(response, ModelResponse) + assert response.status == ResponseStatus.SUCCESS + assert response.data == "Test Model Result" + assert response.completed is True + assert response.used_credits == 0 + assert response.run_time == 0 + assert response.usage is None + + +@pytest.mark.skip(reason="Need to fix model response") +def test_run_sync_polling_error(): + """Test handling of polling errors in the run method""" + model_id = "test-model-id" + base_url = config.MODELS_RUN_URL + execute_url = f"{base_url}/{model_id}".replace("/api/v1/execute", "/api/v2/execute") + + ref_response = { + "status": "IN_PROGRESS", + "data": "https://models.aixplain.com/api/v1/data/invalid-id", + } + + with requests_mock.Mocker() as mock: + # Mock the initial execution call + mock.post(execute_url, json=ref_response) + + # Mock the polling URL to raise an exception + poll_url = ref_response["data"] + mock.get(poll_url, exc=Exception("Polling failed")) + + test_model = LLM(id=model_id, name="Test Model", function=Function.TEXT_GENERATION, url=base_url) + + response = test_model.run(data="test input") + + # Updated assertions to match ModelResponse structure + assert isinstance(response, ModelResponse) + assert response.status == ResponseStatus.FAILED + assert response.completed is False + assert "No response from the service" in response.error_message + assert response.data == "" + assert response.used_credits == 0 + assert response.run_time == 0 + assert response.usage is None + + +def test_run_with_custom_parameters(): + """Test run method with custom parameters""" + model_id = "test-model-id" + base_url = config.MODELS_RUN_URL + execute_url = f"{base_url}/{model_id}".replace("/api/v1/execute", "/api/v2/execute") + + ref_response = { + "completed": True, + "status": "SUCCESS", + "data": "Test Result", + "usedCredits": 10, + "runTime": 1.5, + "usage": {"prompt_tokens": 10, "completion_tokens": 20}, + } + + with requests_mock.Mocker() as mock: + mock.post(execute_url, json=ref_response) + + test_model = LLM(id=model_id, name="Test Model", function=Function.TEXT_GENERATION, url=base_url) + + custom_params = {"custom_param": "value", "temperature": 0.8} # This should override the default + + response = test_model.run(data="test input", temperature=0.5, parameters=custom_params) + + assert isinstance(response, ModelResponse) + assert response.status == ResponseStatus.SUCCESS + assert response.data == "Test Result" + assert response.used_credits == 10 + assert response.run_time == 1.5 + assert response.usage == {"prompt_tokens": 10, "completion_tokens": 20} diff --git a/tests/unit/model_test.py b/tests/unit/model_test.py index 03dccdbe..0b78c319 100644 --- a/tests/unit/model_test.py +++ b/tests/unit/model_test.py @@ -16,11 +16,8 @@ limitations under the License. """ -from dotenv import load_dotenv import requests_mock -load_dotenv() -import re import json from aixplain.utils import config from aixplain.modules import Model @@ -28,8 +25,56 @@ from aixplain.factories import ModelFactory from aixplain.enums import Function from urllib.parse import urljoin - +from aixplain.enums import ResponseStatus +from aixplain.modules.model.response import ModelResponse import pytest +from unittest.mock import patch +from aixplain.enums.asset_status import AssetStatus + + +def test_build_payload(): + data = "input_data" + parameters = {"context": "context_data"} + ref_payload = json.dumps({"data": data, **parameters}) + hyp_payload = build_payload(data, parameters) + assert hyp_payload == ref_payload + + +def test_call_run_endpoint_async(): + base_url = config.MODELS_RUN_URL + model_id = "model-id" + execute_url = f"{base_url}/{model_id}" + payload = {"data": "input_data"} + ref_response = { + "completed": True, + "status": "IN_PROGRESS", + "data": "https://models.aixplain.com/api/v1/data/a90c2078-edfe-403f-acba-d2d94cf71f42", + } + + with requests_mock.Mocker() as mock: + mock.post(execute_url, json=ref_response) + response = call_run_endpoint(url=execute_url, api_key=config.TEAM_API_KEY, payload=payload) + + print(response) + assert response["completed"] == ref_response["completed"] + assert response["status"] == ref_response["status"] + assert response["url"] == ref_response["data"] + + +def test_call_run_endpoint_sync(): + base_url = config.MODELS_RUN_URL + model_id = "model-id" + execute_url = f"{base_url}/{model_id}".replace("/api/v1/execute", "/api/v2/execute") + payload = {"data": "input_data"} + ref_response = {"completed": True, "status": ResponseStatus.SUCCESS, "data": "Hello"} + + with requests_mock.Mocker() as mock: + mock.post(execute_url, json=ref_response) + response = call_run_endpoint(url=execute_url, api_key=config.TEAM_API_KEY, payload=payload) + + assert response["completed"] == ref_response["completed"] + assert response["status"] == ref_response["status"] + assert response["data"] == ref_response["data"] def test_build_payload(): @@ -85,28 +130,26 @@ def test_success_poll(): mock.get(poll_url, headers=headers, json=ref_response) test_model = Model("", "") hyp_response = test_model.poll(poll_url=poll_url) + assert isinstance(hyp_response, ModelResponse) assert hyp_response["completed"] == ref_response["completed"] - assert hyp_response["status"] == "SUCCESS" + assert hyp_response["status"] == ResponseStatus.SUCCESS def test_failed_poll(): with requests_mock.Mocker() as mock: poll_url = "https://models.aixplain.com/api/v1/data/a90c2078-edfe-403f-acba-d2d94cf71f42" headers = {"x-api-key": config.TEAM_API_KEY, "Content-Type": "application/json"} - ref_response = { - "completed": True, - "error": "err.supplier_error", - "supplierError": re.escape( - '{"error":{"message":"The model `` does not exist","type":"invalid_request_error","param":null,"code":"model_not_found"}}' - ), - } + ref_response = {"completed": True, "status": "FAILED", "error_message": "Some error occurred"} + + with requests_mock.Mocker() as mock: mock.get(poll_url, headers=headers, json=ref_response) - test_model = Model("", "") - hyp_response = test_model.poll(poll_url=poll_url) - assert hyp_response["completed"] == ref_response["completed"] - assert hyp_response["error"] == ref_response["error"] - assert hyp_response["supplierError"] == ref_response["supplierError"] - assert hyp_response["status"] == "FAILED" + model = Model(id="test-id", name="Test Model") + response = model.poll(poll_url=poll_url) + + assert isinstance(response, ModelResponse) + assert response.status == ResponseStatus.FAILED + assert response.error_message == "Some error occurred" + assert response.completed is True @pytest.mark.parametrize( @@ -139,15 +182,14 @@ def test_run_async_errors(status_code, error_message): base_url = config.MODELS_RUN_URL model_id = "model-id" execute_url = f"{base_url}/{model_id}" - ref_response = { - "error": "An unspecified error occurred while processing your request.", - } + ref_response = "An unspecified error occurred while processing your request." with requests_mock.Mocker() as mock: mock.post(execute_url, status_code=status_code, json=ref_response) test_model = Model(id=model_id, name="Test Model", url=base_url) response = test_model.run_async(data="input_data") - assert response["status"] == "FAILED" + assert isinstance(response, ModelResponse) + assert response["status"] == ResponseStatus.FAILED assert response["error_message"] == error_message @@ -189,3 +231,303 @@ def test_get_assets_from_page_error(): ) assert "Listing Models Error: Failed to retrieve models" in str(excinfo.value) + + +def test_run_sync(): + model_id = "test-model-id" + base_url = config.MODELS_RUN_URL + execute_url = f"{base_url}/{model_id}".replace("/api/v1/execute", "/api/v2/execute") + + ref_response = { + "status": "IN_PROGRESS", + "data": "https://models.aixplain.com/api/v1/data/a90c2078-edfe-403f-acba-d2d94cf71f42", + } + + poll_response = { + "completed": True, + "status": "SUCCESS", + "data": "Test Model Result", + "usedCredits": 0, + "runTime": 0, + } + + with requests_mock.Mocker() as mock: + mock.post(execute_url, json=ref_response) + + poll_url = ref_response["data"] + mock.get(poll_url, json=poll_response) + + test_model = Model(id=model_id, name="Test Model", url=base_url, api_key=config.TEAM_API_KEY) + + input_data = {"data": "input_data"} + response = test_model.run(data=input_data, name="test_run") + + assert isinstance(response, ModelResponse) + assert response.status == ResponseStatus.SUCCESS + assert response.data == "Test Model Result" + assert response.completed is True + assert response.used_credits == 0 + assert response.run_time == 0 + assert response.usage is None + + +def test_sync_poll(): + poll_url = "https://models.aixplain.com/api/v1/data/mock-model-id/poll" + + in_progress_response = ModelResponse( + status="IN_PROGRESS", data="", completed=False, error_message="", used_credits=0, run_time=0, usage=None + ) + + success_response = ModelResponse( + status="SUCCESS", + data="Polling successful result", + details={"test": "test"}, + completed=True, + error_message="", + used_credits=0, + run_time=0, + usage=None, + ) + + model = Model(id="mock-model-id", name="Mock Model") + + with patch.object(model, "poll", side_effect=[in_progress_response, in_progress_response, success_response]): + + response = model.sync_poll(poll_url=poll_url, name="test_poll", timeout=5) + + assert isinstance(response, ModelResponse) + assert response["status"] == "SUCCESS" + assert response["completed"] is True + assert response["details"] == {"test": "test"} + assert response["data"] == "Polling successful result" + + +def test_run_with_parameters(): + model_id = "test-model-id" + base_url = config.MODELS_RUN_URL + execute_url = f"{base_url}/{model_id}".replace("/api/v1/execute", "/api/v2/execute") + + input_data = "test input" + parameters = {"temperature": 0.7, "max_tokens": 100} + expected_payload = json.dumps({"data": input_data, **parameters}) + + ref_response = { + "completed": True, + "status": "SUCCESS", + "data": "Test Model Result", + "usedCredits": 0, + "runTime": 0, + } + + with requests_mock.Mocker() as mock: + mock.post(execute_url, json=ref_response) + + test_model = Model(id=model_id, name="Test Model", url=base_url, api_key=config.TEAM_API_KEY) + response = test_model.run(data=input_data, parameters=parameters) + + # Verify the payload was constructed correctly + assert mock.last_request.text == expected_payload + assert isinstance(response, ModelResponse) + assert response.status == ResponseStatus.SUCCESS + assert response.data == "Test Model Result" + + +def test_run_async_with_parameters(): + model_id = "test-model-id" + base_url = config.MODELS_RUN_URL + execute_url = f"{base_url}/{model_id}" + + input_data = "test input" + parameters = {"temperature": 0.7, "max_tokens": 100} + expected_payload = json.dumps({"data": input_data, **parameters}) + + ref_response = { + "completed": False, + "status": "IN_PROGRESS", + "data": "https://models.aixplain.com/api/v1/data/test-id", + "url": "https://models.aixplain.com/api/v1/data/test-id", + } + + with requests_mock.Mocker() as mock: + mock.post(execute_url, json=ref_response) + + test_model = Model(id=model_id, name="Test Model", url=base_url, api_key=config.TEAM_API_KEY) + response = test_model.run_async(data=input_data, parameters=parameters) + + # Verify the payload was constructed correctly + assert mock.last_request.text == expected_payload + assert isinstance(response, ModelResponse) + assert response.status == "IN_PROGRESS" + assert response.url == ref_response["url"] + + +def test_successful_delete(): + with requests_mock.Mocker() as mock: + model_id = "test-model-id" + url = urljoin(config.BACKEND_URL, f"sdk/models/{model_id}") + headers = {"Authorization": "Token " + config.TEAM_API_KEY, "Content-Type": "application/json"} + + # Mock successful deletion + mock.delete(url, status_code=200) + + test_model = Model(id=model_id, name="Test Model") + test_model.delete() # Should not raise any exception + + # Verify the request was made with correct headers + assert mock.last_request.headers["Authorization"] == headers["Authorization"] + assert mock.last_request.headers["Content-Type"] == headers["Content-Type"] + + +def test_failed_delete(): + with requests_mock.Mocker() as mock: + model_id = "test-model-id" + url = urljoin(config.BACKEND_URL, f"sdk/models/{model_id}") + + # Mock failed deletion + mock.delete(url, status_code=404) + + test_model = Model(id=model_id, name="Test Model") + + with pytest.raises(Exception) as excinfo: + test_model.delete() + + assert "Model Deletion Error: Make sure the model exists and you are the owner." in str(excinfo.value) + + +def test_model_to_dict(): + # Test with regular additional info + model = Model(id="test-id", name="Test Model", description="", additional_info={"key1": "value1", "key2": None}) + result = model.to_dict() + + # Basic assertions + assert result["id"] == "test-id" + assert result["name"] == "Test Model" + assert result["description"] == "" + + # The additional_info is directly in the result + assert result["additional_info"] == {"additional_info": {"key1": "value1", "key2": None}} + + +def test_model_repr(): + # Test with supplier as dict + model1 = Model(id="test-id", name="Test Model", supplier={"name": "Test Supplier"}) + assert repr(model1) == "" + + # Test with supplier as string + model2 = Model(id="test-id", name="Test Model", supplier="Test Supplier") + assert str(model2) == "" + + +def test_poll_with_error(): + with requests_mock.Mocker() as mock: + poll_url = "https://models.aixplain.com/api/v1/data/test-id" + headers = {"x-api-key": config.TEAM_API_KEY, "Content-Type": "application/json"} + + # Mock a response that will cause a JSON decode error + mock.get(poll_url, headers=headers, text="Invalid JSON") + + model = Model(id="test-id", name="Test Model") + response = model.poll(poll_url=poll_url) + + assert isinstance(response, ModelResponse) + assert response.status == ResponseStatus.FAILED + assert "Expecting value: line 1 column 1" in response.error_message + + +def test_sync_poll_with_timeout(): + poll_url = "https://models.aixplain.com/api/v1/data/test-id" + model = Model(id="test-id", name="Test Model") + + # Mock poll method to always return not completed + with patch.object(model, "poll") as mock_poll: + mock_poll.return_value = {"status": "IN_PROGRESS", "completed": False, "error_message": ""} + + # Test with very short timeout + response = model.sync_poll(poll_url=poll_url, timeout=0.1, wait_time=0.2) + + assert response["status"] == "FAILED" + assert response["completed"] is False + + +def test_check_finetune_status_error(): + with requests_mock.Mocker() as mock: + model_id = "test-id" + url = urljoin(config.BACKEND_URL, f"sdk/finetune/{model_id}/ml-logs") + headers = {"x-api-key": config.TEAM_API_KEY, "Content-Type": "application/json"} + + # Mock error response + error_response = {"statusCode": 404, "message": "Finetune not found"} + mock.get(url, headers=headers, json=error_response, status_code=404) + + model = Model(id=model_id, name="Test Model") + status = model.check_finetune_status() + + assert status is None + + +def test_check_finetune_status_with_logs(): + with requests_mock.Mocker() as mock: + model_id = "test-id" + url = urljoin(config.BACKEND_URL, f"sdk/finetune/{model_id}/ml-logs") + + # Mock successful response with logs using valid ResponseStatus values + success_response = { + "finetuneStatus": AssetStatus.COMPLETED.value, + "modelStatus": AssetStatus.COMPLETED.value, + "logs": [{"epoch": 1.0, "trainLoss": 0.5, "evalLoss": 0.4}, {"epoch": 2.0, "trainLoss": 0.3, "evalLoss": 0.2}], + } + mock.get(url, json=success_response) + + model = Model(id=model_id, name="Test Model", description="") + + # Test with after_epoch + status = model.check_finetune_status(after_epoch=0) + assert status is not None + assert status.epoch == 1.0 + assert status.training_loss == 0.5 + assert status.validation_loss == 0.4 + + # Test without after_epoch + status = model.check_finetune_status() + assert status is not None + assert status.epoch == 2.0 + assert status.training_loss == 0.3 + assert status.validation_loss == 0.2 + + +def test_check_finetune_status_partial_logs(): + with requests_mock.Mocker() as mock: + model_id = "test-id" + url = urljoin(config.BACKEND_URL, f"sdk/finetune/{model_id}/ml-logs") + + response = { + "finetuneStatus": AssetStatus.IN_PROGRESS.value, + "modelStatus": AssetStatus.IN_PROGRESS.value, + "logs": [{"epoch": 1.0, "trainLoss": 0.5, "evalLoss": 0.4}, {"epoch": 2.0, "trainLoss": 0.3, "evalLoss": 0.2}], + } + mock.get(url, json=response) + + model = Model(id=model_id, name="Test Model", description="") + status = model.check_finetune_status() + + assert status is not None + assert status.epoch == 2.0 + assert status.training_loss == 0.3 + assert status.validation_loss == 0.2 + + +def test_check_finetune_status_no_logs(): + with requests_mock.Mocker() as mock: + model_id = "test-id" + url = urljoin(config.BACKEND_URL, f"sdk/finetune/{model_id}/ml-logs") + + response = {"finetuneStatus": AssetStatus.IN_PROGRESS.value, "modelStatus": AssetStatus.IN_PROGRESS.value, "logs": []} + mock.get(url, json=response) + + model = Model(id=model_id, name="Test Model", description="") + status = model.check_finetune_status() + + assert status is not None + assert status.epoch is None + assert status.training_loss is None + assert status.validation_loss is None diff --git a/tests/unit/team_agent_test.py b/tests/unit/team_agent_test.py index fd738c04..56564b73 100644 --- a/tests/unit/team_agent_test.py +++ b/tests/unit/team_agent_test.py @@ -1,8 +1,12 @@ import pytest import requests_mock -from aixplain.modules import TeamAgent +from aixplain.enums.asset_status import AssetStatus +from aixplain.modules import Agent, TeamAgent +from aixplain.modules.agent import ModelTool from aixplain.factories import TeamAgentFactory +from aixplain.factories import AgentFactory from aixplain.utils import config +from urllib.parse import urljoin def test_fail_no_data_query(): @@ -71,3 +75,122 @@ def test_fail_number_agents(): TeamAgentFactory.create(name="Test Team Agent", agents=[]) assert str(exc_info.value) == "TeamAgent Onboarding Error: At least one agent must be provided." + + +def test_to_dict(): + team_agent = TeamAgent( + id="123", + name="Test Team Agent", + agents=[ + Agent( + id="", + name="Test Agent", + description="Test Agent Description", + llm_id="6646261c6eb563165658bbb1", + tools=[ModelTool(function="text-generation")], + ) + ], + description="Test Team Agent Description", + llm_id="6646261c6eb563165658bbb1", + use_mentalist_and_inspector=False, + ) + + team_agent_dict = team_agent.to_dict() + assert team_agent_dict["id"] == "123" + assert team_agent_dict["name"] == "Test Team Agent" + assert team_agent_dict["description"] == "Test Team Agent Description" + assert team_agent_dict["llmId"] == "6646261c6eb563165658bbb1" + assert team_agent_dict["supervisorId"] == "6646261c6eb563165658bbb1" + assert team_agent_dict["plannerId"] is None + assert len(team_agent_dict["agents"]) == 1 + assert team_agent_dict["agents"][0]["assetId"] == "" + assert team_agent_dict["agents"][0]["number"] == 0 + assert team_agent_dict["agents"][0]["type"] == "AGENT" + assert team_agent_dict["agents"][0]["label"] == "AGENT" + + +def test_create_team_agent(): + with requests_mock.Mocker() as mock: + headers = {"x-api-key": config.TEAM_API_KEY, "Content-Type": "application/json"} + # MOCK GET LLM + url = urljoin(config.BACKEND_URL, "sdk/models/6646261c6eb563165658bbb1") + model_ref_response = { + "id": "6646261c6eb563165658bbb1", + "name": "Test LLM", + "description": "Test LLM Description", + "function": {"id": "text-generation"}, + "supplier": "openai", + "version": {"id": "1.0"}, + "status": "onboarded", + "pricing": {"currency": "USD", "value": 0.0}, + } + mock.get(url, headers=headers, json=model_ref_response) + + # AGENT MOCK CREATION + url = urljoin(config.BACKEND_URL, "sdk/agents") + ref_response = { + "id": "123", + "name": "Test Agent", + "description": "Test Agent Description", + "teamId": "123", + "version": "1.0", + "status": "draft", + "llmId": "6646261c6eb563165658bbb1", + "pricing": {"currency": "USD", "value": 0.0}, + "assets": [ + { + "type": "model", + "supplier": "openai", + "version": "1.0", + "assetId": "6646261c6eb563165658bbb1", + "function": "text-generation", + } + ], + } + mock.post(url, headers=headers, json=ref_response) + + agent = AgentFactory.create( + name="Test Agent", + description="Test Agent Description", + llm_id="6646261c6eb563165658bbb1", + tools=[ModelTool(model="6646261c6eb563165658bbb1")], + ) + + # AGENT MOCK GET + url = urljoin(config.BACKEND_URL, f"sdk/agents/{agent.id}") + mock.get(url, headers=headers, json=ref_response) + + # TEAM MOCK CREATION + url = urljoin(config.BACKEND_URL, "sdk/agent-communities") + team_ref_response = { + "id": "team_agent_123", + "name": "TEST Multi agent", + "status": "draft", + "teamId": 645, + "description": "TEST Multi agent", + "llmId": "6646261c6eb563165658bbb1", + "assets": [], + "agents": [{"assetId": "123", "type": "AGENT", "number": 0, "label": "AGENT"}], + "links": [], + "plannerId": "6646261c6eb563165658bbb1", + "supervisorId": "6646261c6eb563165658bbb1", + "createdAt": "2024-10-28T19:30:25.344Z", + "updatedAt": "2024-10-28T19:30:25.344Z", + } + mock.post(url, headers=headers, json=team_ref_response) + + team_agent = TeamAgentFactory.create( + name="TEST Multi agent", + description="TEST Multi agent", + use_mentalist_and_inspector=True, + llm_id="6646261c6eb563165658bbb1", + agents=[agent], + ) + assert team_agent.id is not None + assert team_agent.name == team_ref_response["name"] + assert team_agent.description == team_ref_response["description"] + assert team_agent.llm_id == team_ref_response["llmId"] + assert team_agent.use_mentalist_and_inspector is True + assert team_agent.status == AssetStatus.DRAFT + assert len(team_agent.agents) == 1 + assert team_agent.agents[0].id == team_ref_response["agents"][0]["assetId"]