自定义API端点 (.api.services.)#

预计阅读时间:16分钟

你将学习的内容#

本指南的目标是向数据所有者展示如何扩展现有的Python API以实现:

  • 公开无需审批即可运行的API方法(预批准代码)

  • 创建API桥接以访问托管在第三方平台上的私有资产

简介#

自定义API端点是Python API的扩展,可在数据站点的Syft客户端中使用,允许管理员和数据所有者扩展研究人员可用的方法。

这并不是什么新鲜事:

事实上 - 这正是您在创建Syft函数时所做的事情。例如,如果您定义并提交了以下内容:

@syft_function_single_use(df=some_dataset)
def my_special_method():
    ...

您将扩展我们的.code API,添加只能在获得批准后运行的自定义方法.code.my_special_method()

但在这种情况下,管理员会预先定义数据科学家可以运行的自定义方法,结合上述功能来实现各种目标。

让我们通过几个特别有用的场景来了解这一点。

|:data_scientist:| 1. 数据所有者: 我的私有数据存储在Microsoft Azure SQL数据库中,数据量太大无法迁移到PySyft

假设您在一个SQL数据库中存储了数十TB的数据。如果直接将其上传到PySyft的blob存储,您可能会考虑多个问题:

  • 需要额外工作来托管最新的快照

  • 复制数据成本高昂

  • SQL数据库在处理SQL查询方面非常高效,使用Microsoft Azure SQL引擎进行计算会更经济实惠

就像在PySyft上托管模拟和私有数据集资产一样,您可以定义一个通往模拟和私有数据集的桥梁。

|:data_scientist:| 2. 数据所有者: 我的模型托管在Google Vertex AI上,太大无法迁移到PySyft

假设您将专有模型存储在Google Vertex中。同样,您可能会担心如果直接在PySyft中托管它:

  • 需要额外的工作来托管最新的模型检查点

  • 模型的训练或推理需要大量资源(例如GPU),或者它针对TPU运行进行了优化;或者,简单来说,通过Google Vertex运行计算成本更低。

就像在PySyft上托管模拟和私有模型一样,您可以定义一个通往模拟和私有模型的桥梁。

|:data_scientist:| 2. 数据所有者: 我的模型托管在Google Vertex AI上,太大无法迁移到PySyft

假设您已经了解数据科学家可能需要和请求的内容 - 为了便于他们开展研究,您可以公开一些他们可以使用的公共API端点。

关于如何创建这样一个API桥接到第三方平台的详细操作指南可在此处获取

Hide code cell source
import syft as sy

datasite = sy.orchestra.launch(
    name="test-datasite",
    dev_mode=False,
    create_producer=True,
    n_consumers=1,
    reset=True,
    port="auto",
)

client = datasite.login(email="[email protected]", password="changethis")
client.register(
    email="[email protected]",
    password="verysecurepassword",
    password_verify="verysecurepassword",
    name="New User",
)
guest_client = datasite.login(email="[email protected]", password="verysecurepassword")
Hide code cell output
Starting test-datasite server on 0.0.0.0:54319
Waiting for server to start Done.
SyftInfo:
You have launched a development server at http://0.0.0.0:54319.It is intended only for local use.

Logged into <test-datasite: High side Datasite> as <[email protected]>
SyftWarning:
You are using a default password. Please change the password using `[your_client].account.set_password([new_password])`.

Logged into <test-datasite: High side Datasite> as <[email protected]>

|:data_scientist:| 创建API端点#

公共API端点#

公共API端点本质上是一种无需您批准即可自由使用的方法。这是您公开预先批准方法的途径。

端点的元素包括:

  • 一个带有以下参数的 sy.api_endpoint 装饰器:

    • path: 路径由API名称和子路径组成,格式为api_name.subpath;按此方式定义后,您的API将通过.api.services.api_name.subpath访问

    • description: 支持使用markdown格式详细说明研究人员应如何使用它

    • settings: 可选参数,这是一个字典,包含您可能想在API端点中访问的值。这种方式非常适合传递密钥API,因为这样没有人能看到或访问它们。

  • context: 始终是方法签名的一部分,这允许您访问设置变量或用户客户端,它看起来像这样:

@sy.api_endpoint(
    path="my_api.subpath",
    description="This is an example endpoint",
    settings={"key": "value"}
)
def my_preapproved_func(
    context,
    query: str,
) -> Any:
    pass

让我们来看一个例子:

@sy.api_endpoint(
    path="my_org.sum_numbers",
    description="This is an example endpoint",
)
def my_preapproved_func(
    context,
    number_1,
    number_2
):
    return number_1 + number_2
client.custom_api.add(endpoint=my_preapproved_func)
SyftSuccess:
Endpoint successfully created.

让我们快速测试一下!

client.api.services.my_org.sum_numbers(number_1=2, number_2=3)

指针

5

注意

传递值 由于自定义API端点的定义方式,始终需要完整指定kwargs。

双生API端点#

正如我们之前所见,自定义API端点可以作为桥梁,通过模拟私有范式实现对私有资产的访问。

为此,您需要定义两种方法:一种用于模拟访问,另一种用于私有访问,然后创建最终端点,如下所示:

  • 模拟方法:

@sy.api_endpoint_method(
    settings={"key": MY_API_KEY}
)
def mock_method(context, query:str):
    pass
  • 私有方法:

@sy.api_endpoint_method(
    settings={"key": MY_API_KEY}
)
def private_method(context, query:str):
    pass

通过同时使用两者,您只需定义一个双端点,包括其路径和描述:

new_endpoint = sy.TwinAPIEndpoint(
    path="my_org.my_data",
    mock_function=mock_method,
    private_function=private_method,
    description="This is an example endpoint"
)

让我们看一个示例:

MOCK_STORAGE_API_KEY = ""
PRIVATE_STORAGE_API_KEY = ""

@sy.api_endpoint_method(
    settings={"key": MOCK_STORAGE_API_KEY}
)
def mock_get_sample_method(context, length: int):
    import pandas as pd
    # CALL external API using the key
    # result = call(key=context.settings.key, length=length)
    return pd.DataFrame.from_dict({'data': ['mock_example', 'another_mock_example']})

@sy.api_endpoint_method(
    settings={"key": PRIVATE_STORAGE_API_KEY}
)
def private_get_sample_method(context, length: int):
    import pandas as pd
    # CALL external API using the key
    # result = call(key=context.settings.key, length=length)
    return pd.DataFrame.from_dict({'data': ['private_example', 'another_private_example']})
new_endpoint = sy.TwinAPIEndpoint(
    path="my_org.get_data_sample",
    mock_function=mock_get_sample_method,
    private_function=private_get_sample_method,
    description="Get a fixed number of rows of the data."
)

client.custom_api.add(endpoint=new_endpoint)
SyftSuccess:
Endpoint successfully created.

上下文管理器#

如上所示,方法签名中包含一个context参数。它的作用是帮助您:

  • 设置: 访问设置和任何非公开的密钥,例如API密钥

    示例: context.settings["key]

  • 有状态API: 保持状态 - 用户可以跨同一API的多次运行保存状态,从而能够基于先前状态进行构建或实现诸如速率限制等功能

    示例: context.state["time_of_run"] = time()

  • 访问运行用户的相关元数据

    示例 context.user.email

  • 访问运行用户的客户端: 访问运行用户的客户端以获取各种信息,例如电子邮件,甚至可以代表他们执行操作,例如提交查询。

    示例 context.user_client.code.request_code_execution(execute_query).

  • 您自己的外部代码: 您可以通过`helper_functions`将流程中定义的外部方法或类传递给API端点定义

    示例:

def count_user_runs_in_last_hour(context.state):
    # ...

@sy.api_endpoint_method(
    helper_functions=[count_user_runs_in_last_hour]
):
    context.code.count_user_runs_in_last_hour
    # ...

这为管理员提供了极大的灵活性。如需查看示例,请参考此指南

使用第三方库#

在尝试搭建与外部平台的桥梁时,使用第三方Python包(例如这些平台提供的用于与其API交互的Python库)是非常常见的需求。

在这种情况下,您可以配置自定义API端点,使其始终运行在自定义的工作池(运行适当的镜像)上。可以按如下方式指定:

  • 公共API端点

@sy.api_endpoint(
    path="my_org.sum_numbers",
    description="This is an example endpoint",
    worker_pool="math-libs-pool"
)
def my_preapproved_func(context, number_1, number_2):
    import my_third_party_math_lib
    return number_1 + number_2
  • 双API端点

new_endpoint = sy.TwinAPIEndpoint(
    path="my_org.get_data_sample",
    mock_function=mock_get_sample_method,
    private_function=private_get_sample_method,
    description="Get a fixed number of rows of the data."
    worker_pool="azure-sql-pool"
)

|:data_owner:| |:data_owner:| 查看和使用API端点#

至此,自定义API端点行为的定义工作应该已经完成,我们希望了解如何以管理员、数据所有者或数据科学家的身份来使用它。

请注意,在此情况下管理员和数据所有者拥有相同的权限。

获取所有可用的自定义端点#

所有用户都可以通过以下方式检查可用的端点:

client.custom_api

TwinAPI端点列表

总计: 0

我们甚至可以通过使用上面定义的路径进一步查看各个端点,如下所示:

client.api.services.my_org.get_data_sample

API: my_org.get_data_sample

描述: 获取固定行数的数据。

私有代码:

def private_get_sample_method(context, length: int):
    import pandas as pd
    # CALL external API using the key
    # result = call(key=context.settings.key, length=length)
    return pd.DataFrame.from_dict({'data': ['private_example', 'another_private_example']})

公开代码:

def mock_get_sample_method(context, length: int):
    import pandas as pd
    # CALL external API using the key
    # result = call(key=context.settings.key, length=length)
    return pd.DataFrame.from_dict({'data': ['mock_example', 'another_mock_example']})

查看代码定义#

如上所示,作为管理员,我们可以查看所有已定义的代码。

默认情况下,数据科学家只能看到模拟定义。不过,这可以更新为让数据科学家完全看不到端点的运作方式(请查看下方的更新部分)。

guest_client.api.services.my_org.get_data_sample

API: my_org.get_data_sample

描述:获取固定行数的数据。

私有代码:

N / A

公开代码:

def mock_get_sample_method(context, length: int):
    import pandas as pd
    # CALL external API using the key
    # result = call(key=context.settings.key, length=length)
    return pd.DataFrame.from_dict({'data': ['mock_example', 'another_mock_example']})

直接运行#

对于公共端点,您不需要任何审批 - 可以直接运行并获取最终答案,无需任何人批准您的查询,如下所示:

# As a data scientist / data owner / admin

guest_client.api.services.my_org.sum_numbers(number_1=2, number_2=3)

指针

5

这将创建一个立即执行并返回结果的作业。可以通过client.jobs查看为特定端点创建的作业:

guest_client.jobs

任务列表

总计: 0

然而,对于同时拥有模拟版本和私有版本的Twin Endpoints来说,这是一个包含审批步骤的多阶段流程。

首先让我们看看在没有批准的情况下可以运行什么:

# As a data scientist / data owner / admin
guest_client.api.services.my_org.get_data_sample.mock(length=2)
数据
0 mock_example
1 another_mock_example
# As a data scientist
guest_client.api.services.my_org.get_data_sample.private(length=2)
SyftError: You're not allowed to run this code.
SyftError: You're not allowed to run this code.
SyftError: You're not allowed to run this code.
SyftError:
You're not allowed to run this code.

# As a data owner / admin
client.api.services.my_org.get_data_sample.private(length=2)
数据
0 private_example
1 another_private_example

警告

谁能访问私有数据 我们可以看到,数据科学家仅限于使用公共和模拟的自定义API运行。要运行真实的API,需要数据所有者的批准,这可以通过定义一个syft函数来实现。

在您的syft函数中使用它#

自定义API端点可以像服务器上的其他私有资产一样使用,并且可以作为输入策略的一部分进行指定。

注意

使用.mock.private非常适合测试,类似于与数据集或模型交互的方式。然而,当将其作为输入端点传递时,我们期望您希望针对私有数据进行计算,因此PySyft只允许在获得批准后运行它。

@sy.syft_function_single_use(
    data_endpoint=guest_client.api.services.my_org.get_data_sample)
def get_sample_fixed_length(data_endpoint):
    data = data_endpoint(length=2)
    return data.iloc[0]

guest_client.code.request_code_execution(get_sample_fixed_length)
SyftSuccess:
Syft function 'get_sample_fixed_length' successfully created. To add a code request, please create a project using `project = syft.Project(...)`, then use command `project.create_code_request`.

请求

ID: 4dda4d363f9043a589f6f5eae9ff889c

请求时间:2024-08-24 19:48:04

状态:RequestStatus.PENDING

请求时间: 测试数据站点,类型为数据站点

请求者: 新用户 ([email protected])

变更内容: 请求将 get_sample_fixed_length (资源池ID: default-pool) 的权限状态更改为 RequestStatus.APPROVED。无嵌套请求。

guest_client.code.get_sample_fixed_length()
SyftError:
 Your code is waiting for approval. Code status on server 'test-datasite' is 'UserCodeStatus.PENDING'.

让我们批准并重试:

# as data owner / admin
client.requests[-1].approve()

# as data scientist
result = guest_client.code.get_sample_fixed_length(data_endpoint=guest_client.api.services.my_org.get_data_sample)

result.get()
Approving request on change get_sample_fixed_length for datasite test-datasite
data    private_example
Name: 0, dtype: object

|:data_scientist:| 更新API端点#

更新代码定义#

要更新代码定义,您可以定义一个新的模拟或私有方法并按如下方式更新:

@sy.api_endpoint_method(
    settings={"key": MOCK_STORAGE_API_KEY}
)
def new_mock_get_sample_method(context, length: int):
    import pandas as pd
    return "Oh, look, I updated the mock function!"


client.custom_api.update(endpoint_path="my_org.get_data_sample", mock_function=new_mock_get_sample_method)
SyftSuccess:
Endpoint successfully updated.

client.api.services.my_org.get_data_sample.mock(length=2)

指针

‘哦,看,我更新了模拟函数!’

你可以对private_function进行类似操作。

更新读取代码的权限#

如前所述,默认情况下数据科学家可以访问模拟定义。在某些情况下,这非常有用,能让数据科学家理解模拟响应背后的假设,从而查看代码。

不过,可以通过更新端点的相应设置来隐藏它:

client.custom_api.update(endpoint_path="my_org.get_data_sample", hide_mock_definition=True)
SyftSuccess:
Endpoint successfully updated.

# as data scientist, now:
guest_client.api.services.my_org.get_data_sample

API: my_org.get_data_sample

描述: 获取固定行数的数据。

私有代码:

N / A

公开代码:

N / A

更新超时时长#

这指定了API端点超时的秒数。默认情况下,该值为60秒。

client.custom_api.update(endpoint_path="my_org.get_data_sample", endpoint_timeout=120) # Update to 120s
SyftSuccess:
Endpoint successfully updated.

|:data_scientist:| 删除API端点#

删除一个API端点非常简单:

client.custom_api.delete(endpoint_path="my_org.get_data_sample")
SyftSuccess:
Endpoint successfully deleted.

client.custom_api

TwinAPI端点列表

总计: 0