Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
a7ac7ad
ENG-1852: Hotfix, added fail-fast option (#488)
kadirpekel Apr 11, 2025
b0887bd
Fix: BUG-503 failed to update utility tool after get (#491)
ahmetgunduz Apr 16, 2025
89daf2f
Bug-503-fix Update (#493)
ahmetgunduz Apr 16, 2025
6bf7d00
ENG-1920: added sentry cred (#476)
xainaz Apr 16, 2025
9e2b03c
Read input variables correctly (#496)
thiago-aixplain Apr 17, 2025
409a893
Fix: file not deleted in test_sql_tool_with_csv test (#492)
ahmetgunduz Apr 17, 2025
3acfacd
ENG-2007: Fix agent and team agent parametrized functional test - Syn…
lucas-aixplain Apr 17, 2025
da7ec38
ENG-1789: Add multiple index backbones support (#443)
basitanees Apr 23, 2025
d360780
ENG-2049: rename air functions (#500)
thiago-aixplain Apr 24, 2025
24e88da
ENG 1924: aixplain sdk new test cases for agents using utility and pi…
OsujiCC Apr 24, 2025
044d8d8
add pydantic requirement (#502)
basitanees Apr 25, 2025
4efdbc8
ENG-1978: Adding instructions to teams (#485)
thiago-aixplain Apr 25, 2025
ab2fcc5
BUG-504: Merged paramMappings for the same link vectors (#499)
kadirpekel Apr 25, 2025
268cc1a
ENG-1836: Set name of tools on the SDK (#501)
thiago-aixplain Apr 28, 2025
9796d19
Eng 2051 Improvements on CI flow (#509)
kadirpekel Apr 30, 2025
f0837fc
Add a finetuned version of BGE model (#512)
Muhammad-Elmallah May 5, 2025
511bf5f
ENG-2055-Aixplain-SDK-Centralized-Error-Handling (#510)
ahmetgunduz May 6, 2025
0c4edf4
ENG-1862:Added status to Tools and deployment check for Agent and Tea…
ahmetgunduz May 6, 2025
0989c51
Merge branch 'test' into development
hadi-aix May 8, 2025
84baed8
Fix: BUG-543 SQL Tool upload db issue (#519)
ahmetgunduz May 9, 2025
de6a1c4
ENG-2028: model streaming (#506)
thiago-aixplain May 9, 2025
b909a54
BUG-542-Utility-Model-Update-Test-Failing-in-SDK (#515)
ahmetgunduz May 9, 2025
8aa3d31
ENG-2115 fixing agent tests (#522)
thiago-aixplain May 9, 2025
4f20c65
Merge branch 'test' into development
thiago-aixplain May 9, 2025
87d5791
ENG-1551 ai xplain sdk caching onboarded models pipelines and agents …
xainaz May 12, 2025
6f600d9
Bug 531: standardize asset names (#521)
OsujiCC May 14, 2025
85d0406
fixed asset issue (#526)
xainaz May 15, 2025
4d650c9
Make functional test more stable (#525)
thiago-aixplain May 15, 2025
91d6864
ENG-2100: Enable JSON schema as output format (#513)
thiago-aixplain May 19, 2025
cecd3a6
Added serialize function for save (#529)
xainaz May 20, 2025
6aa648f
Add Embedding Params to Model (#534)
basitanees May 20, 2025
ad4ddbf
Prod 1785 enable adding any embedding in ai r not just embedding mode…
Muhammad-Elmallah May 21, 2025
bb74b68
Changed cache default to false (#536)
xainaz May 22, 2025
e3f072d
Merge branch 'test' into development
thiago-aixplain May 22, 2025
b2a00e3
added filelock (#538)
xainaz May 23, 2025
239b56a
Add filelock to requirements (#540)
thiago-aixplain May 23, 2025
1f6b614
cache duration (#541)
xainaz May 23, 2025
14485e2
ErrorCode returns code in string (#542)
yunsukim86 May 26, 2025
59be7cc
ENG-2003 : Add LLM's to Agents as Object (#524)
ahmetgunduz May 27, 2025
cdaaa81
ENG-2105: persist sql data (#528)
thiago-aixplain May 27, 2025
77ff8d8
Custom inspector interface (#484)
yunsukim86 Jun 2, 2025
f6422db
Eng 2040 ai r 2 add splitting features to the sdk (#546)
Muhammad-Elmallah Jun 2, 2025
59e40d6
pipeline to_dict change + fixed circular imports pipeline functional …
xainaz Jun 2, 2025
c8aa11c
ENG-1962: composio (#547)
thiago-aixplain Jun 4, 2025
b7a929d
Add input target for inspectors (#548)
yunsukim86 Jun 6, 2025
6067f16
ENG-1900 Refined agent deletion error messages (#543)
kadirpekel Jun 10, 2025
c570a70
ENG-2271: Add code interpreter model ID (#550)
lucas-aixplain Jun 16, 2025
80ddb8a
Update enums.py (#552)
hadi-aix Jun 18, 2025
39be1d0
Update enums.py (#554)
hadi-aix Jun 18, 2025
c45b709
Merge branch 'test' into development
hadi-aix Jun 18, 2025
095f9b8
removed pipeline cache test (#556)
xainaz Jun 19, 2025
01a7f9e
Functional test fix of agent in use error (#560)
ahmetgunduz Jun 20, 2025
585444a
BUG-574 Fixed functionType type handling (#561)
kadirpekel Jun 20, 2025
1e84627
PROD-1833: Optional Instructions in Agents (#559)
ahmetgunduz Jun 24, 2025
ea51442
fixing the tests (#569)
Muhammad-Elmallah Jun 25, 2025
ba406e2
Introducing prompt benchmarking (#497)
shreyasXplain Jun 26, 2025
3d6dc91
ENG-2371 functional test for pipeline run_async (#570)
kadirpekel Jun 27, 2025
a96dd07
Add new methods to Air (#578)
basitanees Jul 8, 2025
ac8b5ac
Bumped model-interfaces to latest release (#221)
mikelam-us-aixplain Jul 8, 2025
7e59519
Re-implement the integration.py file (#581)
elsheikhams99 Jul 13, 2025
5b6bcaa
Improve readability - fix OAUTH methods bug (#583)
elsheikhams99 Jul 14, 2025
6ab5953
Eng 2400 add tests, fix composio bugs (#585)
elsheikhams99 Jul 15, 2025
c397aaa
Add MCP Connection (#587)
hadi-aix Jul 15, 2025
ba71caa
Fix integration tests in model_test.py (#590)
elsheikhams99 Jul 16, 2025
7ea5ec8
Eng 2327 integrate mcp server (#592)
hadi-aix Jul 17, 2025
926927c
Update utils.py
hadi-aix Jul 17, 2025
22cde88
Merge branch 'test' into mergetotest_20250717
hadi-aix Jul 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions aixplain/enums/function_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,5 @@ class FunctionType(Enum):
METRIC = "metric"
SEARCH = "search"
INTEGRATION = "connector"
CONNECTION = "connection"
MCPSERVER = 'mcpserver'
MCPCONNECTION = "mcpconnection"
MCP_CONNECTION = "mcpconnection"
MCPSERVER = "mcpserver"
5 changes: 4 additions & 1 deletion aixplain/factories/model_factory/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from aixplain.modules.model.index_model import IndexModel
from aixplain.modules.model.integration import Integration
from aixplain.modules.model.connection import ConnectionTool
from aixplain.modules.model.mcp_connection import MCPConnection
from aixplain.modules.model.utility_model import UtilityModel
from aixplain.modules.model.utility_model import UtilityModelInput
from aixplain.enums import DataType, Function, FunctionType, Language, OwnershipType, Supplier, SortBy, SortOrder, AssetStatus
Expand Down Expand Up @@ -70,8 +71,10 @@ def create_model_from_response(response: Dict) -> Model:
ModelClass = IndexModel
elif function_type == FunctionType.INTEGRATION:
ModelClass = Integration
elif function_type == FunctionType.CONNECTION or function_type == FunctionType.MCPCONNECTION:
elif function_type == FunctionType.CONNECTION :
ModelClass = ConnectionTool
elif function_type == FunctionType.MCP_CONNECTION:
ModelClass = MCPConnection
elif function == Function.UTILITIES:
ModelClass = UtilityModel
inputs = [
Expand Down
5 changes: 4 additions & 1 deletion aixplain/modules/model/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ def __init__(
scope (Text, optional): action scope of the connection. Defaults to None.
**additional_info: Any additional Model info to be saved
"""
assert function_type == FunctionType.CONNECTION or function_type == FunctionType.MCPCONNECTION, "Connection only supports connection function"
assert (
function_type == FunctionType.CONNECTION or function_type == FunctionType.MCP_CONNECTION
), "Connection only supports connection function"

super().__init__(
id=id,
name=name,
Expand Down
6 changes: 6 additions & 0 deletions aixplain/modules/model/integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ def connect(self, authentication_schema: AuthenticationSchema, args: Optional[Ba
id: Connection ID (retrieve it with ModelFactory.get(id))
redirectUrl: Redirect URL to complete the connection (only for OAuth2)
"""
if self.id == "68549a33ba00e44f357896f1":
return self.run({"data": kwargs.get("data")})

if args is None:
args = build_connector_params(**kwargs)

Expand Down Expand Up @@ -131,6 +134,9 @@ def connect(self, authentication_schema: AuthenticationSchema, args: Optional[Ba
f"Before using the tool, please visit the following URL to complete the connection: {response.data['redirectURL']}"
)
return response
else:
raise ValueError(f"Invalid authentication schema: {authentication_schema}")



def __repr__(self):
Expand Down
102 changes: 102 additions & 0 deletions aixplain/modules/model/mcp_connection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
from aixplain.enums import Function, Supplier, FunctionType, ResponseStatus
from aixplain.modules.model.connection import ConnectionTool
from aixplain.modules.model import Model
from typing import Text, Optional, Union, Dict, List


class ConnectAction:
name: Text
description: Text
code: Optional[Text] = None
inputs: Optional[Dict] = None

def __init__(self, name: Text, description: Text, code: Optional[Text] = None, inputs: Optional[Dict] = None):
self.name = name
self.description = description
self.code = code
self.inputs = inputs

def __repr__(self):
return f"Action(code={self.code}, name={self.name})"


class MCPConnection(ConnectionTool):
actions: List[ConnectAction]
action_scope: Optional[List[ConnectAction]] = None

def __init__(
self,
id: Text,
name: Text,
description: Text = "",
api_key: Optional[Text] = None,
supplier: Union[Dict, Text, Supplier, int] = "aiXplain",
version: Optional[Text] = None,
function: Optional[Function] = None,
is_subscribed: bool = False,
cost: Optional[Dict] = None,
function_type: Optional[FunctionType] = FunctionType.CONNECTION,
**additional_info,
) -> None:
"""Connection Init

Args:
id (Text): ID of the Model
name (Text): Name of the Model
description (Text, optional): description of the model. Defaults to "".
api_key (Text, optional): API key of the Model. Defaults to None.
supplier (Union[Dict, Text, Supplier, int], optional): supplier of the asset. Defaults to "aiXplain".
version (Text, optional): version of the model. Defaults to "1.0".
function (Function, optional): model AI function. Defaults to None.
is_subscribed (bool, optional): Is the user subscribed. Defaults to False.
cost (Dict, optional): model price. Defaults to None.
scope (Text, optional): action scope of the connection. Defaults to None.
**additional_info: Any additional Model info to be saved
"""
assert function_type == FunctionType.MCP_CONNECTION, "Connection only supports mcp connection function"
super().__init__(
id=id,
name=name,
description=description,
supplier=supplier,
version=version,
cost=cost,
function=function,
is_subscribed=is_subscribed,
api_key=api_key,
function_type=function_type,
**additional_info,
)

def _get_actions(self):
response = Model.run(self, {"action": "LIST_TOOLS", "data": " "})
if response.status == ResponseStatus.SUCCESS:
return [
ConnectAction(name=action["name"], description=action["description"], code=action["name"])
for action in response.data
]
raise Exception(
f"It was not possible to get the actions for the connection {self.id}. Error {response.error_code}: {response.error_message}"
)

def get_action_inputs(self, action: Union[ConnectAction, Text]):
if action.inputs:
return action.inputs

if isinstance(action, ConnectAction):
action = action.code

response = Model.run(self, {"action": "LIST_TOOLS", "data": {"actions": [action]}})
if response.status == ResponseStatus.SUCCESS:
try:
inputs = {inp["code"]: inp for inp in response.data[0]["inputs"]}
action_idx = next((i for i, a in enumerate(self.actions) if a.code == action), None)
if action_idx is not None:
self.actions[action_idx].inputs = inputs
return inputs
except Exception as e:
raise Exception(f"It was not possible to get the inputs for the action {action}. Error {e}")

raise Exception(
f"It was not possible to get the inputs for the action {action}. Error {response.error_code}: {response.error_message}"
)
32 changes: 32 additions & 0 deletions tests/functional/agent/agent_functional_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -759,3 +759,35 @@ def test_agent_with_action_tool():
assert "helsinki" in response.data.output.lower()
assert "SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL" in [step["tool"] for step in response.data.intermediate_steps[0]["tool_steps"]]
connection.delete()


def test_agent_with_mcp_tool():
connector = ModelFactory.get("68549a33ba00e44f357896f1")
# connect
response = connector.connect(
data="https://mcp.zapier.com/api/mcp/s/OTJiMjVlYjEtMGE4YS00OTVjLWIwMGYtZDJjOGVkNTc4NjFkOjI0MTNjNzg5LWZlNGMtNDZmNC05MDhmLWM0MGRlNDU4ZmU1NA==/mcp"
)
connection_id = response.data["id"]
connection = ModelFactory.get(connection_id)
action_name = "SLACK_SEND_CHANNEL_MESSAGE".lower()
connection.action_scope = [action for action in connection.actions if action.code == action_name]

agent = AgentFactory.create(
name="Test Agent",
description="This agent is used to send messages to Slack",
instructions="You are a helpful assistant that can send messages to Slack. You MUST use the tool to send the message.",
llm_id="669a63646eb56306647e1091",
tools=[
connection,
],
)

response = agent.run(
"Send what is the capital of Finland on Slack to channel of #modelserving-alerts-testing. Add the name of the capital in the final answer."
)
assert response is not None
assert response["status"].lower() == "success"
assert "helsinki" in response.data.output.lower()
assert action_name in [step["tool"] for step in response.data.intermediate_steps[0]["tool_steps"]]
connection.delete()

27 changes: 27 additions & 0 deletions tests/functional/model/run_connect_model_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from aixplain.factories import ModelFactory
from aixplain.modules.model.integration import Integration, AuthenticationSchema
from aixplain.modules.model.connection import ConnectionTool
from aixplain.modules.model.mcp_connection import MCPConnection


def test_run_connect_model():
Expand All @@ -28,3 +29,29 @@ def test_run_connect_model():
action = action[0]
response = connection.run(action, {"text": "This is a test!", "channel": "C084G435LR5"})
assert response.status == ResponseStatus.SUCCESS


def test_run_mcp_connect_model():
# get slack connector
connector = ModelFactory.get("68549a33ba00e44f357896f1")

assert isinstance(connector, Integration)
assert connector.id == "68549a33ba00e44f357896f1"
assert connector.name == ""

url = "https://mcp.zapier.com/api/mcp/s/OTJiMjVlYjEtMGE4YS00OTVjLWIwMGYtZDJjOGVkNTc4NjFkOjI0MTNjNzg5LWZlNGMtNDZmNC05MDhmLWM0MGRlNDU4ZmU1NA=="
response = connector.connect(data=url)
assert response.status == ResponseStatus.SUCCESS
assert "id" in response.data
connection_id = response.data["id"]
# get slack connection
connection = ModelFactory.get(connection_id)
assert isinstance(connection, MCPConnection)
assert connection.id == connection_id
assert connection.actions is not None

action = [action for action in connection.actions if action.code == "SLACK_CHAT_POST_MESSAGE"]
assert len(action) > 0
action = action[0]
response = connection.run(action, {"text": "This is a test!", "channel": "C084G435LR5"})
assert response.status == ResponseStatus.SUCCESS
74 changes: 64 additions & 10 deletions tests/unit/model_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
from aixplain.modules.model.llm_model import LLM
from aixplain.modules.model.index_model import IndexModel
from aixplain.modules.model.utility_model import UtilityModel
from aixplain.modules.model.integration import Integration, AuthenticationSchema
from aixplain.modules.model.integration import Integration, AuthenticationSchema, build_connector_params
from aixplain.modules.model.connection import ConnectionTool, ConnectAction


Expand Down Expand Up @@ -675,6 +675,12 @@ def test_model_not_supports_streaming(mocker):
"pricing": {"price": 10, "currency": "USD"},
"params": {},
"version": {"id": "1.0"},
"attributes": [
{
"name": "auth_schemes",
"code": '["BEARER_TOKEN", "API_KEY", "BASIC"]'
},
]
},
Integration,
),
Expand Down Expand Up @@ -736,14 +742,35 @@ def test_create_model_from_response(payload, expected_model_class):


@pytest.mark.parametrize(
"authentication_schema, name, token, client_id, client_secret",
"authentication_schema, name, data",
[
(AuthenticationSchema.BEARER, "test-name", "test-token", None, None),
(AuthenticationSchema.OAUTH, "test-name", None, "test-client-id", "test-client-secret"),
(AuthenticationSchema.BEARER_TOKEN, "test-name", {"token": "test-token"}),
(AuthenticationSchema.API_KEY, "test-name", {"api_key": "test-api-key"}),
(AuthenticationSchema.BASIC, "test-name", {"username": "test-user", "password": "test-pass"}),
],
)
def test_connector_connect(mocker, authentication_schema, name, token, client_id, client_secret):
def test_connector_connect(mocker, authentication_schema, name, data):
mocker.patch("aixplain.modules.model.integration.Integration.run", return_value={"id": "test-id"})
additional_info = {
'attributes': [
{
'name': 'auth_schemes',
'code': '["BEARER_TOKEN", "API_KEY", "BASIC"]'
},
{
'name': 'BEARER_TOKEN-inputs',
'code': '[{"name": "token"}]'
},
{
'name': 'API_KEY-inputs',
'code': '[{"name": "api_key"}]'
},
{
'name': 'BASIC-inputs',
'code': '[{"name": "username"}, {"name": "password"}]'
}
]
}
connector = Integration(
id="connector-id",
name="connector-name",
Expand All @@ -752,11 +779,16 @@ def test_connector_connect(mocker, authentication_schema, name, token, client_id
supplier="aiXplain",
api_key="api_key",
version={"id": "1.0"},
**additional_info
)
args = build_connector_params(name=name)
response = connector.connect(
authentication_schema=authentication_schema, name=name, token=token, client_id=client_id, client_secret=client_secret
authentication_schema=authentication_schema,
args=args,
data=data
)
assert response == {"id": "test-id"}

assert response["id"] == "test-id"


def test_connection_init_with_actions(mocker):
Expand Down Expand Up @@ -863,14 +895,35 @@ def add(aaa: int, bbb: int) -> int:
)

def get_mock(id):
if id == "67eff5c0e05614297caeef98":
additional_info = {
'attributes': [
{
'name': 'auth_schemes',
'code': '["BEARER_TOKEN", "API_KEY", "BASIC"]'
},
{
'name': 'BEARER_TOKEN-inputs',
'code': '[{"name": "token"}]'
},
{
'name': 'API_KEY-inputs',
'code': '[{"name": "api_key"}]'
},
{
'name': 'BASIC-inputs',
'code': '[{"name": "username"}, {"name": "password"}]'
}
]
}
if id == "686432941223092cb4294d3f":
return Integration(
id="67eff5c0e05614297caeef98",
id="686432941223092cb4294d3f",
name="test-name",
function=Function.UTILITIES,
function_type=FunctionType.INTEGRATION,
api_key="api_key",
version={"id": "1.0"},
**additional_info
)
elif id == "connection-id":
return ConnectionTool(
Expand All @@ -880,10 +933,11 @@ def get_mock(id):
function_type=FunctionType.CONNECTION,
api_key="api_key",
version={"id": "1.0"},
**additional_info
)

mocker.patch("aixplain.factories.tool_factory.ToolFactory.get", side_effect=get_mock)
tool = ToolFactory.create(integration="67eff5c0e05614297caeef98", name="My Connector 1234", token="slack-token")
tool = ToolFactory.create(integration="686432941223092cb4294d3f", name="My Connector 1234", authentication_schema=AuthenticationSchema.BEARER_TOKEN, data={"token": "slack-token"})
assert isinstance(tool, ConnectionTool)
assert tool.id == "connection-id"
assert tool.name == "test-name"
Expand Down